DDD Architecture - Where To Put Common Methods/Hel

2020-08-09 10:39发布

问题:

According to this question on Stack Overflow, in DDD architecture "helper" classes can be in different layers depending on their purpose. For example a helper that formats something in a user friendly way would go in the UI. A database helper would go in infrastructure.

But what about helpers that can be used by more than one layer? For example age calculation. Age might be required in the model layer for business logic. It is used by more than one entity, so it should not be in a specific entity. Also there are places where age is required merely for display purposes in the UI. Similarly, I have string functions that can be used by more than one layer. For example my custom Right and Left methods could be used for formatting in the UI, but they might also be used in the model, for example conditional logic based on a prefix.

So where should these common methods go? My setup is like this:

  • UI
  • Application
  • Model
  • Infrastructure

Model is core and has no dependencies on infrastructure, so the common helpers cannot go in infrastructure. I am considering two options:

1) Have another layer called Common or similar that can be used by any layer. This would create a dependency between Model and Common.

2) Duplicate the helper logic in whatever layer is needed. Eg Have an Age helper in UI AND have an Age helper in the Model. This would violate DRY, but would not require the domain to have a dependency on a "Common" layer.

Which option is better? Is it OK for the Model layer to have a dependency on a "common" layer?

UPDATE:

In the 2.5 years since this question was asked I have concluded:

  1. Things like Right, Left, etc that compensate for limitations in the framework, belong in a "Common" utilities / helper component that even the Model/Domain layer has a dependency on.
  2. The "Common" utilities / helper component should not be very big. With more experience I found that many things I thought of as helpers actually belong in the domain model.
  3. Age belongs in its own class in the domain layer. Much like things like address, phone number and money I consider these things as value objects. Understanding value objects really was key to my understanding of creating reusable domain classes that can be incorportated into other classes.

回答1:

The string helpers are slightly different I think - in that case your custom Right and Left methods are actually compensating for a limitation in the built-in string type of your language/platform - it doesn't have anything to do with your application as such so in this case it's fine to have it globally accessible.

The age calculation example is a little more interesting as it encapsulates behaviour/logic which is very much an intrinsic part of your application/domain. In that case it may be worth evaluating on a case-by-case basis whether it's worth duplicating the behaviour in each layer (violating DRY in favour of SRP). That's a tough decision to make. Although they may be identical now, by duplicating the behaviour you are giving the two methods the chance to diverge from each other in the future. This would typically happen if they had different reasons to change.



回答2:

From my experience, having a CommonInfrastructure (or some such) component/layer which contains all the helper classes/methods is the right way to go. both String helpers and Age helpers as you described can go in there (along with DateTime helpers, dictionary, dynamics, Linq, whatever).

it should be a library that could easily be carried on to the next project, as it would be so generic and without any dependency on current domain logic, and contain useful code for all-purposes.

the only reason to violate DRY would be if you needed Javascript code on the UI and C# code on your backend, in which case you'd need to duplicate that common infrastructure code (unless you come up with a more elaborate solution to keep it DRY).