consider the following simplified example:
public class Ticket
{
public int Id;
public TicketState State;
public Ticket()
{
// from where do I
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.
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;
}
}
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.