doing entity delete in DDD
Asked Answered
D

1

12

I am learning DDD and have this basic question:

It seems with factories, rich domain models, repositories the Create,Read,Update (of CRUD) are taken care of, but what about delete? There could be some business logic around delete of an entity where do you handle that? RepositoryImpl(which belongs to the infrastructure) layer should not bother itself checking those invariants, its job is to remove the given entity from the underlying datastore. This seems to be the diametric opposite of the intent for Factories, but DDD doesn't have something like "kill" factories for delete.

Say there is an Order entity that users can delete, but not until it is in "Fulfilled" state, so a client requesting a delete repo.delete(ent) should get an Exception. Similarly there could be scenarios in which when a client requests a delete it results in an update(may be a status change or setting soft-delete flag).

Where should one handle such scenarios entity.delete() (does it make sense?) or in a Application or domain service called delete. What I am worried is as long as Repository interface has that method called delete, any client can bypass the service methods and call repo methods directly.

Just to add a context, how I would structure my layers is through Java package, and use package visibility as a tool to forbid corrupting interactions amongst layers.

Dufrene answered 1/8, 2012 at 9:15 Comment(7)
Personally, I use entity.Delete() in my Domain Model, and within it I call the repository.Delete(this) at the end, I truly think this make sense since the domain logic must go in the Domain ModelNonchalant
@Nonchalant will that not leak repository to your domain model ?Dufrene
Absolutely not, since my repository is just an interface type that completely hides implementation details from my domain. As in DDD, the repository interface lives in the domain layer, it does make since to use it anywhere within this layer, even if it's inside an entity.Nonchalant
@Nonchalant "the repository interface lives in the domain layer". It can be done this way, but isn't it in typical scenario that the repository is the responsibility of the application service ? (so repository interfaces lives in application layer not in domain layer, implementation is then in infrastructure)Apophasis
@Apophasis Yes it could, but the way DDD was presented in the DDD book the repository interface are part of the domain not app service. Now if you don't agree to the author vision you can do it the way you like, and I know you can find hundreds of 'DDD' implementations that has nothing to do with the original vision.Nonchalant
@Nonchalant Thank you for clarification, I read the Red book but I might look into the Blue book :-)Apophasis
@Apophasis ur welcome, yeah definitely!Nonchalant
B
9

As far as I know there are no specific guidelines for Delete in DDD ('Delete' is a very generic and data-oriented term). It is referred to as 'End of life' and it is a responsibility of the Repository. Most of the time, end of life is not simple and is associated with some business rules, maybe state changes etc. Very often domain objects don't get deleted at all, they just transition to 'Archived' state. I highly recommend reading this article by Udi Dahan.

In order to enforce invariants associated with object's end of life you can structure your code like this:

class Order{
  ...
  bool CanBeArchived(){
    ...
  }
  ...
}

interface OrderArchiver {
  // throws InvalidOperationException if order can not be archived
  void Archive(Order order);
}

class NHibernateOrderArchiver implements OrderArchiver {      
  void Archive(Order order){
    if(!order.CanBeArchived()){
      throw new InvalidOperationException("Order can not be archived.");
    }
    ...
  }
}

Method 'CanBeArchived' can also be a part of 'OrderArchiver' interface if the implementation needs access to other domain objects that are associated with the Order.

Blacklist answered 2/8, 2012 at 16:21 Comment(6)
thanks Dmitry, so does NhibernateOrderArchiver belong to the infrastructure layer ? does this also imply that the repo in this case will not be having a remove(Entity ent) method ? If I get u correctly, u r saying something like to have a domain service that has the method which does this 'End of Life' process as this typically has a lot of domain specific logic associatedDufrene
'Archive' method definition can reside in Repository or in standalone interface. Implementation of the method belongs to infrastructure layer. I would try to avoid introducing domain services if it is possible; only if there is enough logic that you can't put elsewhere. Main point is that it is better to think about 'End of life' and business rules around it (as oppose to data-oriented 'Delete').Blacklist
my main worry here is any logic that is domain specific should not leak out to other layers, i should be able to exercise all logic before i come to a point where i can say(as part of impl) that this entity instance can now be deleted at which point the repo.remove is called, i would like to think of the de-coupling to a point where say another team is developing the infrastructure layer and i am(domain layer developer) completely blind as to what goes inside as are they abt the domain layer.Dufrene
In the example the main logic that detects whether the object is ready for archival is in the domain (CanBeArchived method). Infrastructure layer (archiver implementation) simply calls this logic for a final verification. Infrastructure layer is aware of domain objects, otherwise it would not be able to restore them.Blacklist
@Blacklist cause you put exception "can't be archived" if order "if" returns true.Eosin
@Filip Górny. I updated the code in my answer. This was meant to be a pseudo-code to explain the idea.Blacklist

© 2022 - 2024 — McMap. All rights reserved.