Antipatterns in Domain Driven Design
Complete Developer Podcast - En podcast af BJ Burns and Will Gant - Torsdage
Per wikipedia, “DDD is the concept that the structure and language of software code (class names, class methods, class variables) should match the business domain.” Essentially the idea is that the code and the business logic that it implements will have a shared vocabulary that makes communication between the developers and the stakeholders easier by moving implementation details out of the code that handles business logic. The primary focus of the application is on the core domain and its domain logic. Designs are based on a model of the domain, rather than on a model of the implementation details (such as database structure) that is required to support the domain. This approach allows rapid iteration involving both the domain experts and the development team. It also makes it quicker and easier to onboard developers, as they can look at the code to understand business rules. DDD has a few core principles that are observed. Until you really get very deep into, a useful way to think about it is that it is the application of OOP to business rules. While this is not entirely accurate, it is how you end up with most of the anti-patterns we are about to discuss. Most of these problems really arise because developers have halfway implemented DDD, but haven’t taken it as far as they need to in order to be able to reap all the benefits. If you work for a company that is trying to use DDD, but hasn’t quite internalized all the lessons of it, you’ll probably run into one or more of these anti-patterns as you go through their codebase. It’s important to remember that they at least tried and got part of the way. You simply need to get them the rest of the way to successfully using this excellent approach to building software. Episode Breakdown Primitive obsession Primitive obsession occurs when language primitives (such as strings, integers, etc.) are used instead of small objects for simple tasks. Because the validation and rules around the type aren’t sufficient for your use case, you’ll have to add more rules that live outside the object. A good example of this is using a string for an email address. While email addresses are definitely strings, not all strings are email addresses. This makes it possible for an invalid email address to be set, and also means that you have to keep the validation around email addresses somewhere else. Create custom types (often value objects) to encapsulate the types you are trying to express in code. Include validation logic with the type. You will likely have to make conversions between the primitive type and your type at the edges of your system. This would include API endpoints, database, and file interactions which are not part of your core business rules. Anemic domain models An anemic domain model occurs when entities are built as simple bags of readable properties. This forces the entity logic into other locations in the code. The entities define little or no behavior themselves and can be put into a bad state from outside themselves. An example of this might be a Person object with name, address, phone number etc. on it. If someone wants to change the address, they either interact directly with the properties on the object itself, or make a call to an object that does it for them. This could mean that the object ends up in an invalid state, for instance, if the city in the address was changed, but the postal code was not. Remove setters on public properties, then create methods for discrete actions on the entity to replace what callers were doing. Alter the callers to use these methods. Get rid of empty constructors and create parameterized ones that initialize the entity in a valid state. You’ll probably find that some callers are difficult to refactor, because they orchestrate changes over a set of entities.