Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DDD - Entity state transition

consider the following simplified example:

public class Ticket
{
   public int Id;
   public TicketState State;

   public Ticket()
   {
      // from where do I get the "New" state entity here? with its id and name
      State = State.New;
   }

   public void Finished()
   {
      // from where do I get the "Finished" state entity here? with its id and name          
      State = State.Finished;
   }
}

public class TicketState
{
   public int Id;
   public string Name;
}

The class state is used directly within the domain object ticket. Later in the ticket s lifecycle other states might be set.

The ticket is persisted into a Ticket table, as well as the TicketState. So within the DB the ticket will have a foreign key to the ticket state table.

When setting the appropiate state within my entity, how do I load the state instance from the DB? Do I have to inject a repository into the entity? Do I need to use a framework like castle for such case? Or are there better solutions, maybe passing the state from outside?

public class Ticket
{
   //...
   public ITicketStateRepository stateRep; //<-- inject

   public Ticket()
   {
      State = stateRep.GetById(NEW_STATE_ID);
   }
   //...
}

Is there any best practice? So far I did not use any dependency injection framework or anything and kept any persistence things out of my domain..

Another approch:

public class Ticket
{
   //...

   public Ticket(NewTicketState newTicketState)
   {
      State = newTicketState;
   }
   public void Finished(FinishedTicketState finishedTicketState)
   {
      State = finishedTicketState;
   }
   //...
}
like image 739
Chris Avatar asked Feb 10 '10 23:02

Chris


1 Answers

The Ticket would not have a reference to a repository. It would have a one-to-one relationship with TicketState, and the TicketRepository would simply do the JOIN and map the values into the Ticket.

When I create model objects I usually don't make them aware of whether or not they're persistent, so they aren't injected with a repository. The repository handles all CRUD operations.

Some people object to this, saying that it leads to an anemic domain model; perhaps you're one of them. If that's the case, inject the repository into your Ticket object, but simple ask it to do the JOIN and return a Ticket with its state populated. When you insert or update you have to modify two tables as a single unit of work, so be sure to have transactions turned on.

The reason I like to have CRUD ops outside the domain model object is that it usually isn't the only domain object participating in a use case or transaction. For example, maybe your simple "buy ticket" use case will have a Ticket object, but there might also have to be some other objects that deal with reservations and seating and general ledger and inventory of baggage and all sorts of other things. You'll really want to persist several model objects as a single unit of work. Only the service tier can know when a model object is acting on its own and when it's part of a larger, grander plan.

Update:

Another reason why I don't like the idea of injecting a model object with a DAO so it can handle persistence duties is the trashing of layers and the cyclic dependency it introduces. If you keep model clean from any references to persistence classes you can use them without having to invoke the other layer. It's a one-way dependency; persistence knows about model, but model does not know about persistence.

Inject the persistence into model and they're cyclicly dependent on each other. You can never use or test either one without the other. No layering, no separation of concerns.

like image 57
duffymo Avatar answered Sep 21 '22 16:09

duffymo