Reputation: 822
I'm struggling with a basic issue. The project is in C#, but the issue is general.
I'm following the always valid object principle. As one example I have a product entity that has a mandatory property "ProductCategory". The allowed product categories are user defined and therefore persisted (in a database).
For type safety and better reading I defined Value Objects for various properties to encapsulate the business rules for those types. So there is a ProductCategory class. When creating a ProductCategory instance a factory method checks for example the max length of the string that is passed to the factory before creating the instance. This ensures that each instance of ProdcutCategory is valid.
Simple parameter checks like length are easy and straight forward. My question is where to implement the validation check against the possible values that are persisted. A repository for the allowed values hides the persistence technology and has a method Exists or IsValid.
Option 1) calling the repository from the application layer before calling the factory.
Here the domain layer has no dependency on the repository/infrastructure layer as many advocate. But the factory can't ensure a valid object anymore. It depends on the application layer that the business rule is implemented. Also each command that needs a ProductCategory has to check the repository, which violates DRY.
Option 2) calling the repository from the value object factory
Since factories are part of the domain layer the layer must have access to the repository, which introduces extra coupling. The benefit is that the business validity of ProductCategory is ensured by the object itself and can't be circumvented.
Is there another solution to this dilemma or are there any specific criteria that encourage one or the other option? Would it be OK, if the factory is programmed against an interface of the repository that is saved in the domain layer and only implemented by the repository in the infrastructure layer? I have tried to understand the different approaches, but in this case I feel lost.
Upvotes: 3
Views: 1972
Reputation: 9289
Option 2 seems the better one.
Coupling is not an issue in this case, you are coupling your factory to the query needed to check validation, making this dependency explicit.
Watch out that the only way to not have this checks eventual consistent is having all the data inside a single aggregate. Otherwise there's no way to prevent the fact that the same category is created just after you did the check and before you commit your transaction (this is the real reason of aggregates).
Mind about eventual consistency only if there's a real issue in having it, how often a category is created? how often a product is created? how much is the possibility of a conflict to happen? is there some real issue if that happen?
Most of the times being full consistent is much more expensive than accept eventual consistency.
Upvotes: 2
Reputation: 9257
If I understand correctly, your ProductCategory value object takes care of its own invariants, but whether a particular ProductCategory can be assigned to a particular Product is user defined and stored in the database.
You could use DomainEvents:
This will prevent the transaction completing with an invalid state.
You could use ValidatorAggregate:
If you really want your Product to be "always valid", even before the unit of work is committed, then you could go for:
Try to avoid injecting repositories into your domain model.
Upvotes: 5