diegomtassis
diegomtassis

Reputation: 3657

DDD: Delete on a Repository vs delete on an Entity?

A very simple use case implemented using DDD and java.

I have a FooEntity and a FooRepository. The Entity has a delete method which validates certain state to check whether it is safe to be deleted, and in case this evaluates to true invoke the delete in the repository, which is injected in the entity.

So far so good, but, what happens if somebody invokes the delete method directly in the repository? Then the validation wouldn't be performed.

Placing the validation in the repository would solve the problem, but this would be clearly wrong since it would make necessary to expose the internal state of the entity.

What am I missing?

public class FooEntity {

  @inject
  FooRepository fooRepository;

  private Boolean canBeDeleted;

  public void delete(){
    if (canBeDeleted){
      fooRepository.delete(this);
    }
    throw new CannotBeDeletedException();
  }
}

public class FooRepository {

  @inject
  FooDAO fooDAO;

  public void delete(FooEntity fooEntity){
    fooDAO.delete(fooEntity.getId());
  }
}

Upvotes: 3

Views: 3124

Answers (3)

jett
jett

Reputation: 997

I would put the delete functionality in a domain service.

public class FooService {

  @inject
  FooRepository fooRepository;

  public void delete(Foo foo) {

    if( /* insert validation stuff here to check if foo can be deleted */ ) {
      fooRepository.delete(foo);
    }
 }

The way I do it though is I typically use a ValueObject to represent an Entity's identity. E.g.

public class FooId() {

    String foodId;

    public String FooId(String fooId) {
       this.foodId = foodId;
    }
}

public class Foo() {

    FooId id;

    /* other properties */
}

I would then revise FooService to:

public class FooService {

  @inject
  FooRepository fooRepository;

  public void delete(FooId fooId) {

    foo = fooRepository.retrieve(fooId);

    if( /* insert validation stuff here to check if foo can be deleted */ ) {
      fooRepository.delete(foo);
    }
 }

To delete a foo (assuming fooId was passed by a command from the UI:

 fooService.delete(fooId);

I would not inject a FooRepository in an a class that represents entity. I don't think that it is the rightful place. An Entity for me should not be able to create or delete itself. These functions should be in a Domain Service for that Entity.

Upvotes: 0

Rogério
Rogério

Reputation: 16380

The example code is fine as is (except that it's strange to have a DAO inside a repository class, as "repository" is just a more abstract name for the same concept as the DAO).

You can't really prevent other developers from calling the wrong methods, except for using static analysis code inspections where available.

The repository should only concern itself with removing the given entity instance from the set of persistent entities. It cannot have logic for checking whether the entity is allowed to be deleted or not, even if the isDeletable() method is in the entity class.

Upvotes: 1

Rob Conklin
Rob Conklin

Reputation: 9446

Don't expose the internal state, expose a method like isDeletable() on the entity. The repository's delete can call entity.isDeletable() before deleting, and raise an exception if you are trying to delete an entity that is not deletable. That way you separate the concerns. The entity has the domain knowledge of it's "deletableness", while the repo knows how to delete the entity.

Upvotes: 3

Related Questions