I've been developing applications according to the principles of DDD for a while now, and, as many, I often run into issues when it comes to persisting an aggregate.

One of the main advantages of DDD is that it allows me to use the full power of OO design in my domain -- as such I want to use polymorphism and conform to the open-closed principle. I.e. I can extend my logic by adding new subtypes without requiring changes to the supertypes.

enter image description here

The problem comes when persisting this. Somehow I need to flatten this domain to some persisted representation, and then later restore it. How do I achieve this, without littering the Repository implementation with instanceof (or equivalent in your language of preference) all over the place. This is generally considered bad form, plus it violated OCP, at least in the repository itself. When I add a new subtype, I'm forced to add a case in the repository.

Is there an elegant way to handle this?

  • This question is much too complex to answer here I believe and depends very much on the persistent representation you need. You could just develop a mechanism that can generically serialize an entire object graph with it's data and metadata (perhaps only for the root, like the root's class). Upon deserialization the mechanism would use the metadata of the root to deserialize into the right class and use reflection to inspect the rest of the metadata at runtime in order to reconstruct the rest of the tree. – plalx Dec 6 '16 at 14:43
  • @plalx that would work but that heavily binds the persisted state to the exact layout of the domain model. Ideally you'd need some kind of versioning of the persisted state so that when you evolve the domain the older states can still be loaded. – Joeri Hendrickx Dec 6 '16 at 15:00
  • You need to be more specific. What kind of repository do you want ? An SGBDR which provide both storage and search ? Something that only provide raw storage ? If it's only raw storage, make all your classes serializable, this could be a raw Java class, (or equivalent) or pair of key/value+a pair defining which class it is. – Walfrat Dec 6 '16 at 15:37
  • 1
    Note that DDD is a design technique, not a coding technique. DDD doesn't concern itself much with implementation details. Software developers concern themselves with those details, but that's because they're software developers. DDD itself doesn't have much to say about how you persist things. – Robert Harvey Dec 6 '16 at 16:10
  • 1
    This looks more like the traditional Object Relational mismatch than a DDD/OCP issue. – Erik Eidt Dec 6 '16 at 16:11
up vote 1 down vote accepted

One of the ways is to use reflection. During application startup, the repository would inspect structure of your domain entities and automagically determine relational model and mapping of entities to this relational model. This is basically what most modern ORMs can do. The disadvantage of this approach is that it is extremely complicated to implement for non-trivial cases and one small change in domain model might result in drastically different relational model, which complicates migrations between versions.

Other than that, no, there is no way. If your repository depends on your domain model, it means the repository has to change when domain model changes. That is very definition of dependency. And OCP can only really work if things are not dependent of each other.

But there is a way to make the change easier and safer. Why is usage of instanceof a problem? There is nothing wrong with using it. Its just that if you add new subtype, you don't know where you have to extend the code to support this new subtype. But there is solution for that : a Visitor pattern. With visitor, when you add a new subclass, you also add new method to an interface. This means every class that implements this interface will become non-compilable and you will immediatelly know where everywhere you have to extend the code to support the new class. Yes, it will make adding new subtype harder, but it will make that change safer.

  • 1
    Well, instanceof always leads to fragility of your codebase. Add a subtype and it's broken. Reflection doesn't help because it doesn't allow you to version the schema -- I'm not targeting an RDBMS so ORM makes no sense. I like the solution with the visitor though – Joeri Hendrickx Dec 7 '16 at 10:10

Mark Seeman provides the simple answer to your immediate pain point: at the boundaries, applications are not object oriented.

You might also read Greg Young's essay The Generic Repository.

One of the main advantages of DDD is that it allows me to use the full power of OO design in my domain -- as such I want to use polymorphism and conform to the open-closed principle.

You might need to let that go - there's not a lot of prior art encouraging the use of inheritance in constructing the domain model.

Composition, rather than inheritance, is the more commonly discussed pattern of re-use.

My suggestion: if you really think that you need inheritance, step back for a bit and challenge your domain model; it may be that the ubiquitous language is trying to show you that there are isolated concepts that your current perspective conflates.

  • I agree with composition over inheritance in general, but the polymorphism I encounter in my domain most often is of the "implements interface" type, not "extends class". I feel that throwing that away would mean throwing away one of the main advantages of using OO. +1 for that excellent read from Mark Seeman though. – Joeri Hendrickx Dec 6 '16 at 15:59
  • @JoeriHendrickx keep always in mind that the main goal is to solve your specific problem as efficiently as possible. Inheritance is a powerful feature of OO languages but, if in your concrete situation, it doesn't helps you to achive what you need, easly and efficiently then there's nothing wrong on let it go. Do a step back and look at the problem with perspective. Don't constraint yourself. Be pragmatic. Anyways, the problem of serialization (DTO yes, DTO no) is still there. Try to solve problems, not collect them :-) – Laiv Dec 6 '16 at 16:14
  • @Laiv well yes. But I never said this was a crud or data processing app; the polymorphism in my domain greatly increases the maintainability of it (I drew a simple case here but that's just an example). Without it it would quickly become a sea of if-then-else chains, with all logic shoehorned into the aggregate root, no extendability etc etc. The persistence if a side-thing, and I'd prefer to ditch it;l but we need it just in case of external failure. Anyway I have no issues with making DTOs here, I'm only looking for an elegant way to do it. – Joeri Hendrickx Dec 7 '16 at 7:01
  • If have read somewhere that noSql works well with ddd. The thing is, you store the version of the document, already serialized. At the time of desarialization, frameworks give some facilities to customize the process. In this case these might help you to deal with polymorfism. – Laiv Dec 7 '16 at 7:21

If reflection isn't a dirty word for you, that might be the simplest path. Store full class names (or full serializations) in the database, and use your language's reflection system to instantiate based on name -- or deserialize "directly" if you're storing full serializations.

In many languages, it's just a few extra lines of code in your repositories (or base class) and you're basically done.

All other solutions (Chains of Responsibility, Factories, etc.) require you to register or maintain a list of subclasses explicitly somewhere in code or configuration. And, that's OK for many applications. If you want to avoid reflection, be explicit, or gain some configurability, you just need to decide where you do and don't want to maintain those lists.

I prefer to use some Aggregate-oriented NoSQL database, such like MongoDB, to persist aggregates. Thus you can keep the structure of your model and you don't have to make any mapping and the concrete type of the object is also included as metadata.

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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