I'm implementing a project in DDD using hexagonal architecture.
I'm trying to follow all the best practise avoiding anemic Domain model but I came across this problem.
I have an entity Client that has a property priority, to calculate this priority I use a rest call to an external API.
I'd like to avoid a setPriority method on the entity so I came out with this solution but I'm not sure it is actually "DDD compliant".
Here in pseudo code:
//DOMAIN
interface PriorityService {
public int exec(Client client);
}
class Client {
....
public void calculatePriority(PriorityService service) {
this.priority = service.exec(this);
}
}
//INFRASTRUCTURE
class ThirdPartyPriorityService implements PriorityService{
...
public int exec(Client client) {
int priority = this.externalApi(...);
return priority;
}
}
I find this approach really simple and effective to integrate third party service, the entity is the sole responsible for the coherence of his internal state but I can integrate different systems with ease.
Is there a better way to do it (preferably without the usage of a setter)?
Is there a better way to do it (preferably without the usage of a setter)?
Maybe. Consider:
Design is what we do to get more of what we want than we would get by just doing it -- Ruth Malan
Whether there is a "better way to do it" is going to depend on the "what we want" in your context.
When Eric Evans described ddd in 2003, one of the things he wanted was to separate the domain code from "the plumbing" -- we should be able to describe the business without our code being polluted by database strings, and transactions, and so on.
public void calculatePriority(PriorityService service) {
this.priority = service.exec(this);
}
Riddle: how, in this design, do you deal with the fact that the network is unreliable? If your solution is going to be resilient, then you are going to need logic somewhere that ensures that the system behaves gracefully when the network is unavailable, the remote server is offline for maintenance, credentials go stale, and so on....
Expressing that same idea a different way: fetching an HTTP response from a web server is I/O, and I/O "contaminates" everything that touches it. Therefore ThirdPartyPriorityService is I/O, therefore Client::calculatePriority is I/O.
Same idea again: our HTTP client necessarily lives in the "imperative shell" (Bernhardt 2012), and by injecting the client into our domain code, we are in effect making our domain code part of the imperative shell.
An alternative approach is to recognize that ThirdPartyPriorityService is retrieving information_ from some remote location, and maybe that concern really belongs in the application, rather than in the domain -- your domain code cares about what the information is, but not where it came from.
So you could, for example, change your implementation so that the application invokes the priority service, then passes the priority to the domain model. See Cory Benfield's 2016 talk on protocol libraries.
Designing your code this way isn't better or worse than the other; it's a different set of tradeoffs.
if we failed to make our program align with what we then understood to be the proper way to think about our financial objects, then we were gonna continually stumble over that disagreement and that would slow us down -- Ward Cunningham, 2009
If the code actually works the way you think it works when you aren't looking at it, your estimates about changing it are likely to be better. That might be a valuable "what we want".
Related
I am in the process of refactoring an application and am trying to figure out where certain logic should fit. For example, during the registration process I have to check if a user exists based upon their email address. As this requires testing if the user exists in the database it seems as if this logic should not be tied to the model as its existence is dictated by it being in the database.
However, I will have a method on the repository responsible for fetching the user by email, etc. This handles the part about retrieval of the user if they exist. From a use case perspective, registration seems to be a use case scenario and accordingly it seems there should be a UserService (application service) with a register method that would call the repository method and perform if then logic to determine if the user entity returned was null or not.
Am I on the right track with this approach, in terms of DDD? Am I viewing this scenario the wrong way and if so, how should I revise my thinking about this?
This link was provided as a possible solution, Where to check user email does not already exits?. It does help but it does not seem to close the loop on the issue. The thing I seem to be missing from this article would be who would be responsible for calling the CreateUserService, an application service or a method on the aggregate root where the CreateUserService object would be injected into the method along with any other relevant parameters?
If the answer is the application service that seems like you are loosing some encapsulation by taking the domain service out of the domain layer. On the other hand, going the other way would mean having to inject the repository into the domain service. Which of those two options would be preferable and more in line with DDD?
I think the best fit for that behaviour is a Domain Service. DS could access to persistence so you can check for existence or uniquenes.
Check this blog entry for more info.
I.e:
public class TransferManager
{
private readonly IEventStore _store;
private readonly IDomainServices _svc;
private readonly IDomainQueries _query;
private readonly ICommandResultMediator _result;
public TransferManager(IEventStore store, IDomainServices svc,IDomainQueries query,ICommandResultMediator result)
{
_store = store;
_svc = svc;
_query = query;
_result = result;
}
public void Execute(TransferMoney cmd)
{
//interacting with the Infrastructure
var accFrom = _query.GetAccountNumber(cmd.AccountFrom);
//Setup value objects
var debit=new Debit(cmd.Amount,accFrom);
//invoking Domain Services
var balance = _svc.CalculateAccountBalance(accFrom);
if (!_svc.CanAccountBeDebitted(balance, debit))
{
//return some error message using a mediator
//this approach works well inside monoliths where everything happens in the same process
_result.AddResult(cmd.Id, new CommandResult());
return;
}
//using the Aggregate and getting the business state change expressed as an event
var evnt = Transfer.Create(/* args */);
//storing the event
_store.Append(evnt);
//publish event if you want
}
}
from http://blog.sapiensworks.com/post/2016/08/19/DDD-Application-Services-Explained
The problem that you are facing is called Set based validation. There are a lot of articles describing the possible solutions. I will give here an extract from one of them (the context is CQRS but it can be applied to some degree to any DDD architecture):
1. Locking, Transactions and Database Constraints
Locking, transactions and database constraints are tried and tested tools for maintaining data integrity, but they come at a cost. Often the code/system is difficult to scale and can be complex to write and maintain. But they have the advantage of being well understood with plenty of examples to learn from. By implication, this approach is generally done using CRUD based operations. If you want to maintain the use of event sourcing then you can try a hybrid approach.
2. Hybrid Locking Field
You can adopt a locking field approach. Create a registry or lookup table in a standard database with a unique constraint. If you are unable to insert the row then you should abandon the command. Reserve the address before issuing the command. For these sort of operations, it is best to use a data store that isn’t eventually consistent and can guarantee the constraint (uniqueness in this case). Additional complexity is a clear downside of this approach, but less obvious is the problem of knowing when the operation is complete. Read side updates are often carried out in a different thread or process or even machine to the command and there could be many different operations happening.
3. Rely on the Eventually Consistent Read Model
To some this sounds like an oxymoron, however, it is a rather neat idea. Inconsistent things happen in systems all the time. Event sourcing allows you to handle these inconsistencies. Rather than throwing an exception and losing someone’s work all in the name of data consistency. Simply record the event and fix it later.
As an aside, how do you know a consistent database is consistent? It keeps no record of the failed operations users have tried to carry out. If I try to update a row in a table that has been updated since I read from it, then the chances are I’m going to lose that data. This gives the DBA an illusion of data consistency, but try to explain that to the exasperated user!
Accepting these things happen, and allowing the business to recover, can bring real competitive advantage. First, you can make the deliberate assumption these issues won’t occur, allowing you to deliver the system quicker/cheaper. Only if they do occur and only if it is of business value do you add features to compensate for the problem.
4. Re-examine the Domain Model
Let’s take a simplistic example to illustrate how a change in perspective may be all you need to resolve the issue. Essentially we have a problem checking for uniqueness or cardinality across aggregate roots because consistency is only enforced with the aggregate. An example could be a goalkeeper in a football team. A goalkeeper is a player. You can only have 1 goalkeeper per team on the pitch at any one time. A data-driven approach may have an ‘IsGoalKeeper’ flag on the player. If the goalkeeper is sent off and an outfield player goes in the goal, then you would need to remove the goalkeeper flag from the goalkeeper and add it to one of the outfield players. You would need constraints in place to ensure that assistant managers didn’t accidentally assign a different player resulting in 2 goalkeepers. In this scenario, we could model the IsGoalKeeper property on the Team, OutFieldPlayers or Game aggregate. This way, maintaining the cardinality becomes trivial.
You seems to be on the right way, the only stuff I didn't get is what your UserService.register does.
It should take all the values to register a user as input, validate them (using the repository to check the existence of the email) and, if the input is valid store the new User.
Problems can arise when the validation involve complex queries. In that case maybe you need to create a secondary store with special indexes suited for queries that you can't do with your domain model, so you will have to manage two different stores that can be out of sync (a user exists in one but it isn't replicated in the other one, yet).
This kind of problem happens when you store your aggregates in something like a key-value store where you can search just with the id of the aggregate, but if you are using something like a sql database that permits to search using your entities fields, you can do a lot of stuff with simple queries.
The only thing you need to take care is avoid to mix query logic and commands logic, in your example the lookup you need to do is easy, is just one field and the result is a boolean, sometimes it can be harder like time operations, or query spanning multiple tables aggregating results, in these cases it is better to make your (command) service use a (query) service, that offers a simple api to do the calculation like:
interface UserReportingService {
ComplexResult aComplexQuery(AComplexInput input);
}
That you can implement with a class that use your repositories, or an implementation that executes directly the query on your database (sql, or whatever).
The difference is that if you use the repositories you "think" in terms of your domain object, if you write directly the query you think in terms of your db abstractions (tables/sets in case of sql, documents in case of mongo, etc..). One or the other depends on the query you need to do.
It is fine to inject repository into domain.
Repository should have simple inteface, so that domain objects could use it as simple collection or storage. Repositories' main idea is to hide data access under simple and clear interface.
I don't see any problems in calling domain services from usecase. Usecase is suppossed to be archestrator. And domain services are actions. It is fine (and even unavoidable) to trigger domain actions by usecase.
To decide, you should analyze Where is this restriction come from?
Is it business rule? Or maybe user shouldn't be a part of model at all?
Usualy "User" means authorization and authentification i.e behaviour, that for my mind should placed in usecase. I prefare to create separate entity for domain (e.g. buyer) and relate it with usecase's user. So when new user is registered it possible to trigger creation of new buyer.
I'm trying to follow some of the more current design principles including SOLID and Domain Driven Design. My question is around how people handle "Initializing" Domain Objects.
Here's a simple example:
Based on SOLID, I should not depend on concretions, so I create an interface and a class. Since I'm taking advantage of Domain Driven Design, I create an object with relevant methods. (i.e. not anemic).
Interface IBookstoreBook
{
string Isbn {get; set;}
int Inventory {get; set;}
void AddToInventory(int numBooks);
void RemoveFromInventory(int numBooks);
}
public class BookstoreBook : IBookstoreBook
{
public string Isbn {get; set;}
public int Inventory {get; private set;}
public void AddToInventory(int numBooks);
public void RemoveFromInventory(int numBooks);
}
To help with testing and be more loosely coupled, I also use an IoC container to create this book. So when the book is created it is always created empty. But, if a book doesn't have an ISBN and Inventory it is invalid.
BookstoreBook(string bookISBN, int bookInventory) {..} // Does not exist
I could have 4 or 5 different classes that use a BookstoreBook. For one,
public class Bookstore : IBookstore
{
...
public bool NeedToIncreaseInventory(BookstoreBook book) { ...}
...
}
How does any method know is getting a valid book? My solutions below seem to violate the "Tell Don't Ask" principle.
a) Should each method that uses a Bookstore book test for validity? (i.e. should NeedToIncreaseInventory test for a books validity? I'm not sure it should have to know what makes a BookstoreBook valid.)
b) Should I have a "CreateBook" on the IBookstoreBook object and just "assume" that clients know they have to call this anytime they want to initialize a BookstoreBook? That way, NeedToIncreaseInventory would just trust that "CreateBook" was already called on BookstoreBook.
I'm interested in what the recommended appreach is here.
First off, I think your BookstoreBook doesn't have any really relevant methods, which means it doesn't have any relevant behavior, no business rules at all. And since it doesn't contain any business rules it actually is anemic. It just has a bunch of Getters and Setters. I would argue that having a method like AddToInventory that ends up just adding +1 to a property is no meaningful behavior.
Also, why would your BookstoreBook know how many of its type are in your Bookstore? I feel like this is probably something the Bookstore itself should keep track of.
As for point a): no, if you're creating books from user input you should check the provided data before you even create a new book. That prevents you from ever having invalid books in your system.
As for the creation of the object, the question is will you ever have more than one book type? If the answer is no you can drop the interface and just instantiate a book in a class that is responsible for creating new books from user input for example. If you need more book types an abstract factory may be useful.
First of all, a great way to make sure that entity state only can be set by behavior (methods) so to make all property setters private. It also allows you to make sure that all related properties are set when the state changes.
But, if a book doesn't have an ISBN and Inventory it is invalid.
There you have two business rules. Let's start with ISBN. If a book is not valid without it it HAVE to be specified in the constructor. Otherwise it's fully possible to create a book which is invalid. An ISBN also have a specified format (at least the length). So that format have to be validated too.
Regarding the inventory I believe that it's not true. You might have books that are sold out or books that can be booked before their release. Right? So a book CAN exist without an inventory, it's just not likely.
If you look at the relation between inventory and books from the domain perspective they are two separate entities with different responsibilities.
A book is representing something that the user can read about and use that information to decide whether it should be rented or purchased.
An inventory is used to make sure that your application can fulfill your customers request. Typically it can be done by a delivery directly (decrease the inventory) or by a backorder (order more copies from your supplier and then deliver the book).
Thus the inventory part of the application do not really need to know everything there is to know about the book. Thus I would recommend that the inventory only knows about the book identity (that's typically how root aggregates can reference each other according to Martin Fowler's book).
An inversion of control container is typically used to to manage services (in DDD the application services and the domain services). It's job is not to act as a factory for domain entities. It will only complicate things without any benefit.
To help with testing and be more loosely coupled, I also use an IoC container to create this book.
Why is your IoC container creating books? That's a bit strange. Your domain model should by container agnostic (wiring together the interfaces and the implementation is the concern of your composition root).
How does any method know is getting a valid book?
The domain model knows it is getting a valid book, because it says so right there in the interface.
The data model knows it is producing a valid book, because the constructor/factory method accepted its arguments without throwing an exception.
Should each method that uses a Bookstore book test for validity?
No, once you have a Book, it is going to stay valid (there shouldn't be any verbs defined in your domain model that would create an invalid data model).
Should I have a "CreateBook" on the IBookstoreBook object and just "assume" that clients know they have to call this anytime they want to initialize a BookstoreBook? That way, NeedToIncreaseInventory would just trust that "CreateBook" was already called on BookstoreBook.
It's normal to have a factory for creating objects. See Evans, chapter 6.
books can be created from a database and many other places. I'm assuming others have had to solve this issue if they are using DDD and I am wondering about their approach. Should we all be using factories - as you suggest that take the needed data as input?
There are really only two sources for data -- your own book of record (in which case, you load the data via a repository), and everywhere else (where you need to make sure that the data conforms to the assumptions of your model.
Based on SOLID, I should not depend on concretions
If you're referring to the Dependency Inversion principle, it does not exactly say that.
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions.
No domain entity is of a higher level than another and normally no object in the Domain layer is a "detail", so DIP usually doesn't apply to domain entities.
I also use an IoC container to create this book
Considering that BookstoreBook has no dependency, I'm not sure why you would do that.
How does any method know is getting a valid book?
By assuming that the book is Always Valid, always consistent. This usually requires having a single Book constructor that checks all relevant rules at creation time, and state-changing methods that enforce invariants about the Book.
a) ...
b) ...
You're mixing up two concerns here - making sure that Book is in a consistent state wherever it is used, and initializing a Book. I'm not sure what your question is really about in the end, but if you apply the "always valid" approach and forget about Book being an interface/higher level abstraction, you should be good to go.
I'm used to develop software creating "domain entities" -- these entities "depends" only on other entities inside the domain.
Let's say I have this interface
package domain;
import domain.beans.City;
public interface CitiesRepository {
City get(String cityName);
}
As you can see, the City I am returning is again a domain object. Implementations of this CitiesRepository can be found outside the domain, and can rely upon a database, an http client, a cached decorator etc.
I am now working with a reactive framework -- vert.x -- and I am trying to understand how I can keep working using such model. I don't want vert.x specific answer but only to understand if there is any pattern/best practice to understand how to achieve this.
In the reactive-programming there is almost never a return value but always some callback/handler that will consume the event after it happened. Should I rewrite my interfaces to be "reactive"?
package domain;
import domain.beans.City;
public interface CitiesRepository {
void get(String cityName, DomainHandler<City> cityHandler);
}
Just providing this example caused me some some spaghetti-headache when thinking to the implementations where I have to deal with the Handler of the "reactive framework" to "fill" my domain handler.
Should I stop thinking in this kind of design when working with reactive model? Should I prefer an Observable/Promise approach?
Any hint would be really appreciated
In the reactive systems I've been involved with there has been an event handler that would then use the repository:
public void SomeEventHandler : IHandle<SomeEvent> {
public SomeEventHandler(CityRepository repo) {}
}
You would then use your existing repository inside the handler code:
public void When(SomeEvent event) {
var city = _cityRepository.Get(event.CityName);
// do something with city
}
In the CompositionRoot of the application, the handler would be registered to handle the event through whatever messaging bus / reactive stream etc. will be receiving / producing the event.
So I wouldn't look to make the repository reactive, rather add in an event handler to use it.
With reactive design you add a layer of indirection of how the API is invoked and you specify that in addition to your original vanilla spec. The reason is, in async design it matters a lot how you invoke stuff and it is not always one size fits all, so better to not make early big decisions, or bind "what it is/does" to "how it does it".
There are three common tools for making things asynchronous:
future/promise
callback
message passing
Future/promise is the most binding of the three in terms of the whole design and is usually the most hairy on the implementation side and you need to do a lot of moves to prevent ABA bugs in your design and to truck futures which are still running something, but no one needs the results. Yes, they abstract away concurrency, are monadic etc., but they make you their hostage the moment you add the first one and they are quite hard to get rid of.
Callback is the fastest in a single process, but to make them work with actor-based system or across wires you inevitably are going to use messages. Moreover, the moment you need a first state machine you need event queue and messages right away. So to be most future proof, the safest path is to just go with messages. Moving between message and callback is very straightforward (when possible at all) for how simple both these mechanisms are.
A protocol to lookup city by key could be something like this:
protocol Requests
message GetCityRequest(name): Requests
protocol Responses
message GetCityResponse(cityMaybe): Responses
But knowing this topic really well, I'd say invest into the "state replication pattern" in generic form and use it for both simple static lookups and dynamic subscriptions. It is not hard to get it right and it will be your main working horse for most of your system needs.
I'm looking for some advice on how much I should be concerned around avoiding the anemic domain model. We are just starting on DDD and are struggling with analysis paralysis regarding simple design decisions. The latest point we are sticking on is where certain business logic belongs, for example we have an Order object, which has properties like Status etc. Now say I have to perform a command like UndoLastStatus because someone made a mistake with an order, this is not as simple as just changing the Status as other information has to be logged and properties changed. Now in the real world this is a pure administration task. So the way I see it I have two options I can think of:
Option 1: Add the method to order so something like Order.UndoLastStatus(), whilst this kinda make sense, it doesn't really reflect the domain. Also Order is the primary object in the system and if everything involving the order is placed in the order class things could get out of hand.
Option 2: Create a Shop object, and with that have different services which represent differant roles. So I might have Shop.AdminService, Shop.DispatchService, and Shop.InventoryService. So in this case I would have Shop.AdminService.UndoLastStatus(Order).
Now the second option we have something which reflects the domain much more, and would allow developers to talk to business experts about similar roles that actually exists. But its also heading toward an anemic model. Which would be the better way to go in general?
Option 2 would lead to procedural code for sure.
Might be easier to develop, but much harder to maintain.
Now in the real world this is a pure administration task
"Administration" tasks should be private and invoked through public, fully "domain`ish" actions. Preferably - still written in easy to understand code that is driven from domain.
As I see it - problem is that UndoLastStatus makes little sense to domain expert.
More likely they are talking about making, canceling and filling orders.
Something along these lines might fit better:
class Order{
void CancelOrder(){
Status=Status.Canceled;
}
void FillOrder(){
if(Status==Status.Canceled)
throw Exception();
Status=Status.Filled;
}
static void Make(){
return new Order();
}
void Order(){
Status=Status.Pending;
}
}
I personally dislike usage of "statuses", they are automatically shared to everything that uses them - i see that as unnecessary coupling.
So I would have something like this:
class Order{
void CancelOrder(){
IsCanceled=true;
}
void FillOrder(){
if(IsCanceled) throw Exception();
IsFilled=true;
}
static Order Make(){
return new Order();
}
void Order(){
IsPending=true;
}
}
For changing related things when order state changes, best bet is to use so called domain events.
My code would look along these lines:
class Order{
void CancelOrder(){
IsCanceled=true;
Raise(new Canceled(this));
}
//usage of nested classes for events is my homemade convention
class Canceled:Event<Order>{
void Canceled(Order order):base(order){}
}
}
class Customer{
private void BeHappy(){
Console.WriteLine("hooraay!");
}
//nb: nested class can see privates of Customer
class OnOrderCanceled:IEventHandler<Order.Canceled>{
void Handle(Order.Canceled e){
//caveat: this approach needs order->customer association
var order=e.Source;
order.Customer.BeHappy();
}
}
}
If Order grows too huge, You might want to check out what bounded contexts are (as Eric Evans says - if he had a chance to wrote his book again, he would move bounded contexts to the very beginning).
In short - it's a form of decomposition driven by domain.
Idea is relatively simple - it is OK to have multiple Orders from different viewpoints aka contexts.
E.g. - Order from Shopping context, Order from Accounting context.
namespace Shopping{
class Order{
//association with shopping cart
//might be vital for shopping but completely irrelevant for accounting
ShoppingCart Cart;
}
}
namespace Accounting{
class Order{
//something specific only to accounting
}
}
But usually enough domain itself avoids complexity and is easily decomposable if You listen to it closely enough. E.g. You might hear from experts terms like OrderLifeCycle, OrderHistory, OrderDescription that You can leverage as anchors for decomposition.
NB: Keep in mind - I got zero understanding about Your domain.
It's quite likely that those verbs I'm using are completely strange to it.
I would be guided by the GRASP principles. Apply the Information Expert design principle, that is you should assign the responsibility to the class that naturally has the most information required to fulfill the change.
In this case, since changing the order status involves other entities, I would make each of these low-level domain objects support a method to apply the change with respect to itself. Then also use a domain service layer as you describe in option 2, that abstracts the whole operation, spanning multiple domain objects as needed.
Also see the Facade pattern.
I think having a method like UndoLastStatus on the Order class feels a bit wrong because the reasons for its existence are in a sense outside of the scope of an order. On the other hand, having a method which is responsible for changing the status of an order, Order.ChangeStatus, fits nicely as a domain model. The status of an order is a proper domain concept and changing that status should be done through the Order class, since it owns the data associated with an order status - it is the responsibility of the Order class to keep itself consistent and in a proper state.
Another way to think of it is that the Order object is what's persisted to the database and it is the 'last stop' for all changes applied to an Order. It is easier to reason about what a valid state for an order might be from the perspective of an Order rather than from the perspective of an external component. This is what DDD and OOP are all about, making it easier for humans to reason about code. Furthermore, access to private or protected members may be required to execute a state change, in which case having the method be on the order class is a better option. This is one of the reasons why anemic domain models are frowned upon - they shift the responsibility of keeping state consistent away from the owning class, thereby breaking encapsulation among other things.
One way to implement a more specific operation such as UndoLastStatus would be to create an OrderService which exposes the domain and is how external components operate upon the domain. Then you can create a simple command object like this:
class UndoLastStatusCommand {
public Guid OrderId { get; set; }
}
An the OrderService would have a method to process that command:
public void Process(UndoLastStatusCommand command) {
using (var unitOfWork = UowManager.Start()) {
var order = this.orderRepository.Get(command.OrderId);
if (order == null)
throw some exception
// operate on domain to undo last status
unitOfWork.Commit();
}
}
So now the domain model for Order exposes all of the data and behavior that correspond to an Order, but the OrderService, and the service layer in general, declare the different kind of operations that are performed on an order and expose the domain for utilization by external components, such as the presentation layer.
Also consider looking into the concept of domain events which considers anemic domain models and ways of improving them.
It sounds like you are not driving this domain from tests. Take a look at the work of Rob Vens, especially his work on exploratory modeling, time inversion and active-passive.
In this question someone replies "You never let the domain object implementations call services by themselves!". Is this statement a hard fast rule of DDD or does it depend on your own application and architecture?
Contrived example:
As an example lets suppose we have a UserImage object in our model that gets populated from an uploaded image by a user. And then lets suppose that we can submit this image to a 3rd party service that can identify thumb prints and return a Guid if a match is found.
public IThumbPrintService {
Guid FindMatch(Bitmap image);
}
public class UserImage {
public Bitmap Image {get; set;}
public Guid ThumbPrintId {get; set;}
public bool FindThumbPrintMatch() {
// Would you call the service from here?
ThumbPrintId = _thumbPrintService.FindMatch(this.Image);
return ! ThumbPrintId.CompareTo(Guid.Empty);
}
}
public class RoboCopUserImageService : IUserImageService {
// Or move the call to a service method
// since it depends on calling a separate service interface
public bool FindThumbPrintMatch(UserImage userImage) {
userImage.ThumbPrintId = _thumbPrintService.FindMatch(userImage.Image);
return !userImage.ThumbPrintId.CompareTo(Guid.Empty);
}
}
What is avoided or gained by not letting domain objects call services themselves?
EDIT: Are there any good online articles that discuss this specific topic?
This is the Spreadsheet Conundrum: does the phone dial the phone number, or does the phone number dial itself on the phone?
You might find Double Dispatch to be interesting reading, though overkill in your situation, I reckon.
The Single Responsibility Principle is often at odds with the OO tenet of Tell, Don't Ask. My feeling on the subject has oscillated, and I have settled on the following conditions when logic should go into a domain object:
The logic should be domain logic, i.e. nothing like image.load() or image.save()
The logic shouldn't give the object more than "one reason" to change in the domain. This is a re-statement of the SRP.
The logic shouldn't be forced into the object. In other words, if you end up doing something like trying to calculate the sum of some property in a list of domain objects without using a getter, think twice.
In your situation, I'd opt against putting the call to the service inside the entity object, mainly because the service doesn't seem like it is related to your domain, but more related to persistence. Domain objects should be coupled to domain concepts, and I don't think the service you gave qualifies.
An example where I think calling a service in an entity might be acceptable would be if your application used a third-party workflow server to manage parts of its state. Essentially, this is the State Pattern with the states defined at run-time.
I think it is acceptable to have domainObject.moveToNextState() (assuming this code "makes sense" in your ubiquitous language) call the service that talks to your server because the workflow server manages a part of the domain model.
I'll add that DDD is very interested with following the language of the domain. Do you hear domain experts saying "A user image finds if its thumb print matches those in the XYZ vendor service"? Or do they say "The XYZ vendor service, given a thumb print, indicates whether that thumb print exists"? Go with the one that makes the most sense in your domain.
Some more thoughts (I've thought about this issue a lot because it is central to design):
In the Evans DDD book, an Account
entity has methods like
credit(Amount), debit(Amount), transferTo(Account, Amount) and
accrue(), but a FundsTransferService has a transfer(Account, Account, Amount) method. The transferTo method doesn't call any service, but merely handles the logic that involves Accounts, like crediting and debiting the right amounts.
The FundsTransferService, in addition to co-ordination, has its own rules to check, rules that don't fit into Accounts. The exact amount to credit or debit might involve outside parties. This makes it awkward for transferTo to call the service.
For simple objects, like the UserImage, significant domain logic that can fit in the object itself might be scarce because it isn't, as far as I can tell, an Aggregate. Aggregates, I think, present more of an opportunity to house domain logic. The Account example is likely an Aggregate.
One disadvantage I see is that allowing your domain object to call services may make it harder to serialize, or at least cause some issues after serializing it when someone on the other side calls its service method(s).
If you allow an Entity Object to call a service it is performing two roles Data Object and Service Object. Generally, each object should have on responsibility, not only in implementation but also in usage.
In your case, the lowly UserImage seems to be both an Image and a ThumbPrint Recognizer.
I think it's better not to call repositories or services from entities or value objects, but sometimes it's necessary, for example, if an entity has to return other entity which should be loaded from database but it cannot navigate to it using object graph. Then dependency inversion principle comes to help, which means that entities and value objects depend on interfaces of services and repositories and not on implementations.