The other day I received some feedback on the way the Domain Model in the Autumn of Agile series is shaping up. The relevant parts of the e-mailed comments are as follows…
I am a little confused about where to put the business rules. Into model objects or in a higher level layer (such as a service layer) ? For example take AddSkill method on Employee class in Skill Project. Logically, user cannot assign same SkillType to an Employee. Business rules are changeable, and for that they should handled on a higher layer as principle.
First, as I told the commenter, thanks very much for the feedback; I really appreciate the input, especially when it goes to such a great point! Because this tension between assigning responsibilities to either services or domain entities in your domain model is a frequent point of confusion among software engineers everywhere, I asked for and received approval to turn my response to him into a blog post so that others could understand my thought process on this kind of decision, so here goes…
Patterns for Business Logic in Domains
I think that the reason there is disagreement (or at least different opinions) on the Internet about the direction to follow in regards to the ‘proper’ location for business rules is that the simple answer is (like most software architecture choices): "it depends" .
The issue of ‘services’ in a Domain Model is a tricky one; its (IMHO) one of the areas in which DDD references (like Evans, Fowler, etc.) tend to offer the poorest guidance about ‘when something becomes a service’. At the extremes, you have one of the following two approaches…
- The ‘everything-is-in-a-service‘ pattern: every business rule is represented in one or more services. The services are responsible for ‘choreographing’ the interaction between your domain entities and enforcing business rules between them.
- The ‘everything-is-in-the-entity‘ pattern: all business rules are encapsulated in the domain entities and the services are relegated to the role of controlling interaction between your domain entities and the rest of the world outside your specific domain itself (like the canonical TaxCalculationService example, etc.)
Anemic Domain Model *IS* an Anti-Pattern
I am NOT a fan of the anemic domain model and DO believe it to be an anti-pattern in most cases. The approach in #1 above most often leads directly to an anemic domain model unless (as some will suggest) you take the approach of injecting one or more services into your domain entities as they are constructed. While this can seem like it mitigates the anemic domain model anti-pattern, its really just about ‘where you type the code’ that enforces the business rules and (IMHO) doesn’t use ‘domain services’ the way they were really intended.
If you are coding domain services just to inject them into one or more domain entities, this may indeed seems to be a good way to encapsulate the business logic into services (for re-use, testing, etc.). But if you then inject them into your entities, then (again) the business logic is ‘in’ your entities since the entities have to know when to invoke the services they have been handed.
Coining a New Term: ‘Service-Ignorance’
To a degree, this is the same debate about whether entities should be persistent-ignorant: should entities also be ‘service-ignorant’‘? The same reasons people (tend to) argue against injecting repositories into entities is the same reason I would usually argue against injecting services into them — in a sense a repository is a just a ‘persistence-service’ for your domain. Services and Repositories can (and often need to be) aware of the entities they manipulate, but entities (usually) shouldn’t be aware of the services or repositories that manipulate THEM.
In pattern #2 above, by placing the business rules into the actual entities, it becomes much simpler (IMO) to develop, test, and for the developer to consume (in the app) the business logic. Business rules in most systems tend to be what we mean when we say ‘behavior’ of our objects and to avoid an anemic domain model, our entities need to have both data AND behaviors. This is why I *tend* to place the business logic into the domain entities themselves: its cleaner from testing and consuming standpoint.
For example, as a service, to do AddSkill(…) I would need to do something like….
Employee emp = new Employee(); Skill skill = new Skill(); SkillService skillService = new SkillService(); skillService.AddSkillToEmployee(emp, skill);
In this case, skill and emp are just ‘dumb data containers’ (really just the old-style DTO-anti-pattern) and I do consider this to be an instance of the anemic domain model anti-pattern. I feel (in general) that business rules that are about enforcing relationships between domain entities (as AddSkill(…) does) are perfectly fine in the entities themselves since it reduces the above code to just…
Employee emp = new Employee(); Skill skill = new Skill(); emp.AddSkill(skill);
…which is (IMHO) a lot clearer, easier to understand, and an acceptable encapsulation of a business rule about enforcing the relationship between an employee and a skill.
So When do I need a Domain Service?
As a contrasting situation, here is an example of a business rule that I wouldn’t really (probably) want to encapsulate in an employee…
Employee emp = new Employee(); Skill skill = new Skill(); PromotionService promotionService = new PromotionService(); promotionService.PromoteEmployee(emp, PromotionService.EmployeeLevels.SeniorDeveloper);
In this case, the service is being invoked to make a ‘fundamental change’ to an employee by promoting them to some new ‘rank’ or ‘position’ in the company (the enum represented by ‘SeniorDeveloper’ in this contrived example). Since its entirely likely that promoting an employee to another rank would mean that more than just the emp instance would need to change (e.g., let’s say that this would also mean that their salary, their vacation time, etc. would also need to change — and in ways it would probably make NO sense for the employee object to be aware of), it seems way too much for the Employee class to ‘know’ about all of those business rules (which, BTW, might –as suggested– change over time significantly and so should be well-encapsulated into a service).
That’s Just my Opinion; I Could be Wrong!
I think where I’m headed here (again, this is just how *I* approach this stuff; there are certainly no shortage of other opinions out there!) is that when the business rule is purely about the entity at the root of the aggregate enforcing rules on its consistent components within it, its (generally) OK for that business rule to be expressed within the entity. But when the business rule begins to affect multiple aggregate roots and require the ‘coordination’ of work between aggregates, that’s when I tend to opt for expressing that logic in a domain service instead of inside the domain entities themselves.
I hope this helps clarify my thoughts on this issue. Great question and keep ’em coming, everyone~!
Steve,
Have you taken a look at or worked with McCafferty’s S#arp Architecture (http://code.google.com/p/sharp-architecture/)? I was wondering if you were heading in that direction with the Autumn series or if you had any comments on it. It looks to solve a lot of the plumbing issues and allows the dev to focus on business value issues. Do you see the Autumn series as a stepping stone towards something more full featured like S#arp?
Interesting post Steve,
I have actually gone around my domain in a very simmilar way as of recently. I tend to put all my rules in the domain and just use my application services to delegate the tasks.
I guess where I have gone on a *different* route is that in your example of PromoteEmployee, I would have this on my domain, and the service would delegate to the domain. I could obviously be wrong in doing this but to me it feels right to have these rules in 1 place and 1 place only in the domain.
As another example I have a StudyPlan and this needs to be submitted, once submitted an email is sent to the user and the admin staff, so I made a SubmitPlan method on the domain, I then inject my Mailer objects into my domain model, and let my domain send the emails, now really this might have been better to do in the service, but in my mind sending these emails is a business rule. So I put this all in the domain where going with my theory of stuffing as much business logic into my domain is kept true :).
My 2 cents, as I say I might be going about this the total wrong way as I am new at this whole DDD thing but I am happy with how things are working for me at the moment and have not hit any major issues.
Cheers
Stefan
@Stefan:
I think that whether you are ‘new’ to DDD, experiences with years of DDD background, or somewhere in between (which is certainly where I find myself), there is room for a pretty wide variety of interpretations about how exactly one goes about making just this kind of decision.
You raise an interesting set of points. In your case where you suggest that PromoteEmployee(…) be modeled in some domain entity, what domain entity would you see comtaining this logic? The Employee class or something else…?
The reason I ask is that it seemed to me that PromoteEmployee(…) in Employee would require Employee to know waaaaay too much about the rest of the domain model. Would you suggest in this case that the PromotionService be injected into the Employee class and Employee take a dependency on IPromotionService so that Employee wouldn’t be coupled to a concrete instance of the service but would ‘know’ via the interface the method(s) to call on the service to affect its own promotion? Just wondering…
@tbrooks:
Sorry; missed your comment there. I usually try to be a bit more responsive than this~!
Though I’ve glanced briefly at some content around it in the past, I haven’t looked in detail at the newer S#arp Architecture per-se. I have in the past read most of Bill’s ORM-related posts on places like CodeProject.com with interest as I think most of his guidance is very valuable in re: implementation patterns.
I have noticed various posts and some increased activity around his work with S#arp Architecture recently so I will give it a more detailed review and comment when I have some spare time. Based on his past work, I have every reason to believe that this too is certain to contain some valuable patterns and usage paradigms around some of the technology that I hold dear 😀
Thanks for the heads-up.
Steve,
Great post. This was an issue I was struggling with over the weekend (coincidentally, I posted a question about this to the S#arp Architecture Google Groups board a couple days ago). The answers I received there largely coincide with your approach. I agree that the services-only ends up with a very anemic domain and is just a step up from having business logic completely within codebehinds / controller actions. The closer you can tie your business rules to your domain, the easier it’s going to be going forward both from a testing perspective and a maintenance perspective (too many services leads to major headaches down the road).
Steve,
I guess I did not think the Promote employee scenario in as much detail as I would have liked, but I would say inject the service into the domain model and the domain calls the service, I guess doing this has no real benefit does it?
And yes I would have put this on the Employee model, so Employee.PromoteTo(Role newRole);
This would check the new role and make a decision on what to do, but I see where we are getting that the domain is knowing too much, I think I did not 100% think this one out, I guess what is promote doing too, if it is only a couple of steps like change status and send an email I would inject a mailer into the domain and just do it in the domain.
But if promotion means change level, adjust salary, send emails etc etc. I think you are right this is not good in the domain, maybe then we can inject the promotion service into the domain and then Employee.Promote calls out to the domain service, but we can still do some business rules validation in the domain before we call out to the service.
I just like have a method on the Employee called Promote, because logically to me we are Promoting the employee itself, if we promote through a service itself we get the same effect but it feels natural to me to just call Employee.Promote, so I would have the EmployeeService have a Promote(empId, newLevel) method which would select the employee call Promote on the domain model, this way my application services just delegate work to my domain, and I can still have the PromotionService as a domain service which might do some work, i.e. send emails, modify payroll but this is called from my domain.
Once again something I have been experimenting with I might be well off the mark here :), hope I made some sense.
Cheers
Stefan
@Stefan:
This is actually an interesting mind-game to play out; I think I see where you are headed…
In your suggestion, there would STILL be a PromotionService in the domain, but the actual act of performing the promotion on the Employee is performed by the Employee instance…?
So the PromotionService might have to call Employee.Promote(…), VacationRepository.IncreaseEmployeTimeOff(…), etc., etc. when an Employee was promoted.
This way, the PromotionService is still doing the high-level ‘choreographing’ of the whole collection of domain entities involved in the ‘promotion’ process, but the actual act of the promotion ON each domain entity is still the responsibility of each individual domain entity.
I think I like it; elegant, encapsulated, testable, and reasonable. Did I follow that correctly –?
Steve,
Yep glad we are on the same page here, the service is just delgating the work elsewhere, I tend to make my services do nothing else, just push the work elsewhere.
IMHO it seems like a nice way to go about things, and keeps any business logic in our domain. I see services as a mediator and I try to keep them doing the minimal work possible, ultimately pushing as much work into my domain.
So you don’t think I am going the wrong way about this :)?
Cheers
Stefan
@Stefan:
No, actually I think that this is a perfectly fine way to attack the problem. I can see the benefits of this approach.
The one thing that I really still cannot completely reconcile about the translation of many of Evans’ ideas into actual practice however is the idea of whether Repositories and Services and their like are supposed to be ‘full-fledged’ members of the domain or are supposed to be ‘relegated to the sidelines’ as ‘second-tier’ members of the Domain.
I’ve been over the Evans book many times since my first read of it and I cannot find anywhere that its stated that Entities and Value objects hold primacy and Repositories and Services are second-class citizens in the collection of available Domain ‘building blocks’. Yet that’s pretty much what I see over and over again in implementations (your suggestion as well as many of my own projects too, frankly).
One can find thread after thread on the Yahoo DDD discussion group where participants are vehemently against placing any business logic into repositories. To me, this immediately makes them ‘dumb data-persistence-arbiters’ in the overall model which necessarily means they are second-class citizens as they lack any real ability to participate in the ‘business value’ part of the app. But since this has (for years) been an accepted model of abstracting persistence, I think I’m inclined to go with the flow on this one.
But Domain Services, IMHO, are a completely different animal and that’s what I think leads to the huge gray area around their ‘proper’ (in the context of DDD) implementation. Should they be full members of the domain, and as such its perfectly fine for them to retain some business responsibility? Or should they be relatively dumb and light-weight participants, delegating all the ‘real’ work to Entities (as you are suggesting)?
It seems to me that there are valuable benefits to either approach and the easy answer (in DDD or anything else) would always seem to be ‘whatever is appropriate in your situation’, but this huge gray area (specifically around the role of services in the domain) will continue to be a point of fuzziness in my understanding of what Evans meant when he described the parts of a Domain Model, irrespective of how people have actually taken those elements and put them into actual practice.
Thanks for the input (as always); great discussion~!
Steve,
Thanks that gives me a lot to think about, the funny thing is I have not read Evans book yet :(, I really think I should get to this ASAP, I think I will go an pick this up this week and get stuck into it, hopefully will give me some more value. Although I find that learning by doing and discussing with people who have done this in ‘real’ projects helps me more than a book ever will (I am not one that picks up things from books, more of a do it kind of guy).
I actually can see what you are saying about domain services, should they actually do more work, thinking of it maybe they should, I treat my services as more of a delegation master might you say at the moment, whether they be domain services or application services. They both really just push work elsewhere and never do more than 4 or 5 tasks, i.e. use assembler to map DTO to entity, validate entity, and then call repository to update.
But thinking of it say we have an shopping cart, the Cart domain object will have a CalculateTotal method, I would inject a ITaxCalculationService into my Cart domain object, then the CalculateTotal would so something like:
CalculateTotal() {
total = this.Items.Sum(item => item.Price);
tax = this.taxCalculationService.CalculateTax(total, this.User.Country);
return total + tax;
}
So for example, the tax service might take the total and the country (silly example most likely but it gets the point across I think), then TaxService would then do something like this obviously might be dynamic and use db for tax rates etc.
CalculateTax(total, country) {
if(country == australia) {
total = total * 1.1
} else {
total = total * 2.0
}
return total
}
So I think this is a good example where the domain service will maybe perform some business rules in it, and it makes sense to do this too and would not use an entity to do the work.
Mind my language used above I am calling it Stefan#, and yes as usual thanks for the good discussion I always get a lot of value from our long chats :).
Cheers
Stefan
Any clue(s) when the next installment of “Autumn of Agile” might come out? Been waiting for almost 5 months now.
Hi,
Which Evans’ book are you guys referring to?
Thanks
@RT:
Domain Driven Design; Tackling Complexity in the Heart of Software, by Eric Evans.
If you’re serious about DDD, then this is *the* seminal work in the field.
http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215
[…] Where does my business logic go? […]