views:

216

answers:

3

Hi all,

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;
   }
   //...
}
+1  A: 

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.

duffymo
+1 for persistence ignorant objects. Just because the domain model has been polluted with repository objects, doesn't mean it is not anemic - in fact, it might hide places where domain objects aren't carrying their weight.
David Hall
Thanks but maybe my question wasn't clear enough. I was asking how to set the appropiate state entity when e.g. the ticket is instanstiated or its state is changed. Can you post an example on how you would solve the above problem?
Chris
The instantiation state is easy enough: it ought to be NEW. Something has to be orchestrating events to change its state. I usually call it a service, because it implements a particular use case. It will own an instance of the repository, which it'll use to either instantiate a new Ticket or read an existing one, change its state to satisfy the use case, persist the new state as a single unit of work, and end the use case.
duffymo
@David Hall - indeed, a robust domain model object might not be responsible for its persistent state. There are many richer and more interesting behaviors than CRUD operations.
duffymo
A: 

This answer hopefully follows on from duffymo's.

In a DDD view of the world, your TicketState is an entity that is part of the Ticket aggregate (where a ticket is the aggregate root).

Following from that, your TicketRepository deals with both Tickets, and TicketStates.

When you retrieve a Ticket from the persistence layer you then allow your TicketRepository to retrieve the state from the DB and set it on the ticket correctly.

If you are newing up a ticket then (I think) you do not need to touch the database yet. When the ticket is eventually persisted, you than take the New state of the ticket and persist it correctly.

Your domain classes should not need to know anything about the database model that takes care of state, they should only need to know about the domain model view of state. Your repository is then responsible for this mapping.

David Hall
But what if I instantiate a new ticket, which is not yet persisted and want to display its state name (stored in the DB) on the user interface? Where do I get that information from?
Chris
@Chris - see my comment below.
duffymo
A: 

To me, a simple key-value pair that represents state in the database (or whatever persistence medium) doesn't need to be modeled as such in the domain. In the domain, I would make TicketState an enum, and make it the responsibility of the ITicketRepository to know how to map that to the requirements of the database schema.

Within the ticket repository, you can have a cache of ticket state IDs keyed on TicketState, that is lazy-loaded into a static variable (just one approach) from the database. The ticket repository would map the Ticket.State value to IDs from that cache for inserts/updates.

namespace Domain {
  public class Ticket {
    public Ticket() { State = TicketStates.New; }
    public void Finish() { State = TicketStates.Finished; }
    public TicketStates State {get;set;}
  }

  public enum TicketState { New, Finished }
}

namespace Repositories {
  public class SqlTicketRepository : ITicketRepository {
    public void Save(Ticket ticket) {
      using (var tx = new TransactionScope()) { // or whatever unit of work mechanism
        int newStateId = TicketStateIds[ticket.State];
        // update Ticket table with newStateId
      }
    }
  }

  private Dictionary<TicketState, int> _ticketStateIds;
  protected Dictionary<TicketState, int> TicketStateIds{
    get {
      if (_ticketStateIds== null) 
        InitializeTicketStateIds();
      return _ticketStateIds;
    }
  }

  private void InitializeTicketStateIds() {
    // execute SQL to get all key-values pairs from TicketStateValues table
    // use hard-coded mapping from strings to enum to populate _ticketStateIds;
  }
}
gWiz
As the model stands I agree with this - however, if the state transition needs to evolve so it can express business logic then having a State entity can make a lot of sense.
David Hall
But what if I instantiate a new ticket, which is not yet persisted and want to display its state name (stored in the DB) on the user interface? Where do I get that information from?
Chris
Easy- perist it after you instantiate it and before you display to the UI, of course. Why would you show the state of a persistent object before it was saved? You don't display the outcome of any operation to a user until it's complete, and it's not complete until it's persisted.
duffymo
My answer assumes that the names in the database are storage-level details. If those exact names are actually presentation data, then you could go with duffymo and David Hall's answers. However, unless the app must be able to modify those string values, I would actually prefer to put them in resource (resx) files, either in the domain or presentation layers.
gWiz