1

I’m using a hexagonal/clean/ports-and-adapter architecture. For the sake of simplicity, I’ll only talk about domain and infrastructure here, where the infrastructure has dependencies on the domain but the domain is unaware of the infrastructure.

My application (infrastructure) receives events. The infrastructure layer maps them to the domain objects and calls a use case. The use case uses the data from the domain object to calculate something and then saves it back.

As the domain layer is unaware of the infrastructure, it only calls a save(domain object) method in an interface.

I implemented the interface in the infrastructure layer and uses a real database.

So far so good, but I’d also like to save some metadata from the event along with the domain object. Let’s say I need the event timestamp in order to avoid outdated events overriding newer data.

How can I save the metadata without polluting my domain layer? The event timestamp is not part of the domain; the domain should not know about events at all.

My current “solution” is not to call a save method in the use case. Instead, the use case returns the updated domain object and the use case caller (infrastructure), then maps the domain object back to the database entity and adds the additional information from the event.

In my mind, that’s not a suitable solution. Saving should be part of the use case.

Any suggestions on this?

2
  • 2
    Cross-cutting concerns such as logging, security, etc. are an unavoidable reality. Mixins, decorators, visitors, base classes, transactions, AOP etc. are all attempts to achieve that reality in an architecturally-coherent way. "Pure" architecture is a myth. Commented Sep 30, 2022 at 14:24
  • 1
    I don't see the problem. Whatever is encoded here save(domain object) will do the job. if your domain model and persistene model is the same model, then that's a different story.
    – Laiv
    Commented Oct 31, 2022 at 8:06

2 Answers 2

1

Your current solution and your additional goal are at odds with one another.

  • Based on the Infra->Domain->Infra flow, you've inherently stated that the two Infra components are separate and unrelated things. They individually implement a different interface from the Domain layer.
  • Now you are stating that there is some data that needs to be transferred between these two Infra components, and it can't travel via the Domain because it's not domain-related. Therefore, you are now stating that these two Infra interactions need to have a more direct relation to one another, which clashes with your earlier architecture where this was explicitly not the case.

Your initial architecture is not capable of handling what you actually need done, without breaking its own rules. Something has got to give here.

How you solve this is up to you - I can't tell you which part of the conflicting givens is the one that's either wrong or the weakest link.

Possible solutions include:

  • Developing a domain concept so that the domain can actually accommodate this information
  • Grouping the two infra components in a single interface. The interface itself does not know about events, but because a single instance will now provide both components, the concrete class is able to store and recall the event data. In other words, the infra layer looks up the event data (in its own private cache) when a save is called.
    • How you handle a save when no event data can be found in the cache is up to you to decide.
  • Find a different way to prevent operations that are out of order being applied, such as a version number in your item (this is common for event streams). This requires more information than the current question provides to properly analyze.
  • There are use cases where "metadata" is not strongly typed (e.g. http headers), and instead a key-by-convention approach is used. For example, you could have a Dictionary<string,object> in your domain object. The domain isn't aware of any specific key values being used, but the infra components are able to write/read metadata according to their own privately managed key values. This keeps the domain agnostic while still allowing it to carry the metadata.
    • How you handle a save when the metadata cannot be found in the dictionary is up to you to decide.
-1

So far so good, but I’d also like to save some metadata from the event along with the domain object. Let’s say I need the event timestamp in order to avoid outdated events overriding newer data.

Using this example wouldn't a better way to avoid this problem be to ensure your events are always read in order? Also isn't it better to store data you actually need?

This chapter from Architecture Patterns with Python might help you with designing your aggregates.

https://www.cosmicpython.com/book/chapter_07_aggregate.html

1
  • No one chooses to process data out of order if they can instead process it in order by simply choosing to do so. This answer isn't addressing the question, it's questioning (or misunderstanding) its premise.
    – Flater
    Commented Oct 30, 2022 at 23:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.