I've recently watched a few Pluralshigh courses on DDD by Vladimir Khorikov. He was encouraging to create a rich instead of anemic domain models. It all looked very nice in a small test-project, however I still have no idea how to put extensive business logic inside rich domain model.

In rich domain model we are supposed to put domain logic into entities. So let's say we want to model an Employer who pays the salary the their Employees. I see Employer as the aggregate root here.

So we add a Employer.PayTo(someEmployeeIdentificator) method. Business rules for calculating the salary could be very elaborate and depend on things like:

  • Employer and Employees countries
  • Employment form
  • Employee taxation form
  • How much did an Employee work last month
  • and so much more, you get the point

Potentially, there could be a few dozens of algorithms. Some might even require communication with external services. A perfect case for strategy pattern, but:

  • The logic was supposed to be implemented inside the entities
  • Employees are hidden inside the Employer aggregate root
  • I cannot use IOC to inject stuff into my entities (they are usually created by some ORM). And providing the dependency trees is no fun.
  • Injecting the some implementations of SalaryCalculator to the Employer entity might be a bad idea, as the calculators might not be 'pure'. They might have references to some external resources (e.g. issue tracker)

How would you model it?

有帮助吗?

解决方案

The logic was supposed to be implemented inside the entities

Yes, although it's usually more specific than that - the logic that computes the new state of an entity is supposed to be "inside" the entity.

Employees are hidden inside the Employer aggregate root

That may not be an effective model to use for this kind of problem; it might make more sense to focus on the transactions (exchanges of money), or the ledgers, rather than on the people.

I find it helpful to remember that aggregates are digital things; they are (parts of) documents that describe something, not the thing itself.

I cannot use IOC to inject stuff into my entities

Remember, inversion of control "is really just a pretentious way of saying taking an argument." We don't usually inject stuff into entities, that's true. Instead, we pass stuff to the entities as arguments (domain services).

Injecting the some implementations of SalaryCalculator to the Employer entity might be a bad idea, as the calculators might not be 'pure'. They might have references to some external resources (e.g. issue tracker)

The early discussions of domain model (Evans in the blue book, Fowler in Patterns of Enterprise Application Architecture) don't call for it to be pure. It's objects calling other objects, with the expectation of side effects.

As best I can tell, you really have two options - you can make the orchestration among objects part of the domain model, or you can treat the model itself as just a single collaborator, and manage the orchestration elsewhere.

How would you model it?

"It depends". I'd probably begin by separating out different processes

  • Calculating how much compensation has accrued during some pay period
  • Calculating the dispersal of some amount of compensation
  • Tracking and confirming the asset transfers

Additionally, pay close attention to correctly modeling subtle distinctions; if salaried compensation, hourly compensation, and commissioned sales exist in your domain, then they should be separately identifiable in your model. "Almost the same" is just a wishy-washy way of spelling different.

其他提示

In rich domain model we are supposed to put domain logic into entities

The problem with this sentence is, people interpret it wrong as

  • "we are supposed to put all domain logic into entities".

A better interpretation would be:

  • "we are supposed to put some domain logic into entities"

where "some" means the domain logic which makes most sense. There is no "hard & fast" rule to make a decision where to draw the line, but a good start is to put any logic which can be expressed mostly through the properties of the object itself into the object.

As you already demonstrated, a large operation like PayTo does not fall into that category, so I would recommend to implement it not directly in the Employer class. That does not mean you will end up with an anemic domain model, surely when your program evolves, you will find lots of small operations which fit perfectly into an Employer class.

I see Employer as the aggregate root here.

When you do OO modeling, usually a business function is defined on the subject of the action, not the initiator of the action.

It depends on the requirements of course, but I would perhaps model this as Employee.receiveSalary().

Another indicator that this may be "better", is that in your case the method needs the employee id (basically a reference to another object) to do its work. It indicates that the method wants to be in another object.

Injecting the some implementations of SalaryCalculator...

I would probably not create an object named SalaryCalculator. The problem with that is, it is not part of the "domain". Even if you want to create different "strategies", you are better off finding a way to incorporate it into the domain itself.

This might be as easy as just naming the thing Salary instead of SalaryCalculator. There can be HourlySalary, MonthlySalary, whatever. That makes sense in the domain instead of tacking it on.

The logic was supposed to be implemented inside the entities

Yes. If you can not find a good place to do things, you are probably missing a good abstraction. (See example with SalaryCalculator). If you do it right, you will not need any services or calculators or other things that are not part of the domain.

Employees are hidden inside the Employer aggregate root

If that is a problem, then just don't do that. Proper modeling comes first, technical things come second! Maybe the Employee should be the aggregate root, or there is a third abstraction that can act as a root for this specific case.

I cannot use IOC to inject stuff into my entities...

Again, proper modeling first, technology second. If the technology does not support a correct model, you can choose another technology (there are plenty).

Objects collaborate with eachother, saying that a set of objects can't define collaborators seems unreasonable to me.

You don't need your business entities to be the same than your persistence entities. You can use any tool for making the mapping between them easily (in C#, we have Automapper).

This way you can inject any dependency you need into your business entities. Besides, it can help you hide properties/attributes which you might have made public just because your ORM needed them.

许可以下: CC-BY-SA归因
scroll top