2010-02-10 2 views
8

рассмотрит следующий пример: упрощенногоDDD - Объект переход состояния

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; 
} 

состояние класса используется непосредственно в билете объекта домена. Позже в жизненном цикле билета могут быть установлены другие состояния.

Билет сохраняется в таблице билетов, а также в TicketState. Таким образом, в БД билет будет иметь внешний ключ в таблице состояния билета.

При настройке подходящего состояния внутри моего объекта, как загрузить экземпляр состояния из БД? Нужно ли мне вводить репозиторий в объект? Нужно ли использовать такой фреймворк, как замок для такого случая? Или есть лучшие решения, может быть, переход извне?

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

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

Есть ли передовая практика? До сих пор я не использовал рамки инъекции зависимостей или что-нибудь и все любые сохраняемости вещи из моего домена ..

другой Approch:

public class Ticket 
{ 
    //... 

    public Ticket(NewTicketState newTicketState) 
    { 
     State = newTicketState; 
    } 
    public void Finished(FinishedTicketState finishedTicketState) 
    { 
     State = finishedTicketState; 
    } 
    //... 
} 

ответ

4

Билет не будет иметь ссылку на хранилище. Он будет иметь отношение «один к одному» с TicketState, а TicketRepository просто сделает JOIN и сопоставляет значения в Ticket.

Когда я создаю объекты модели, я обычно не информирую их о том, постоянны они или нет, поэтому они не снабжаются репозиторием. Репозиторий обрабатывает все операции CRUD.

Некоторые люди возражают против этого, говоря, что это приводит к anemic domain model; возможно, вы один из них. Если это так, вставьте репозиторий в свой объект Ticket, но попросите его сделать JOIN и вернуть билет с его заполненным состоянием. Когда вы вставляете или обновляете, вам необходимо изменить две таблицы как единое целое, поэтому обязательно включите транзакции.

Причина, по которой мне нравится иметь CRUD-операции вне объекта модели домена, заключается в том, что она обычно не является единственным объектом домена, участвующим в прецеденте или транзакции. Например, может быть, ваш простой вариант «купить билет» будет иметь объект Ticket, но также могут быть некоторые другие объекты, которые занимаются резервированием и размещением, общей книгой и инвентарем багажа и всевозможными другими вещами. Вы действительно хотите сохранить несколько объектов модели как единое целое. Только уровень обслуживания может знать, когда объект модели действует сам по себе и когда он является частью более крупного плана.

Update:

Еще одна причина, почему мне не нравится идея впрыскивать модели объекта с DAO, чтобы он мог справиться сохраняемости обязанности является громя слоев и циклическая зависимость он вводит. Если вы сохраните модель в чистоте из любых ссылок на классы персистентности, вы можете использовать их без необходимости ссылаться на другой уровень. Это односторонняя зависимость; Настойчивость знает о модели, но модель не знает о настойчивости.

Внедрение персистентности в модель, и они циклически зависят друг от друга. Вы никогда не сможете использовать или протестировать один без другого. Никакого расслоения, никакого разделения проблем.

+0

+1 для настойчивости неосведомленных объектов. Просто потому, что модель домена была загрязнена объектами репозитория, не означает, что она не анемия - на самом деле она может скрыть места, где объекты домена несут свой вес. –

+0

Спасибо, но, возможно, мой вопрос был недостаточно ясным. Я спрашивал, как установить подходящую государственную сущность, когда, например, билет инициируется или изменяется его состояние. Можете ли вы опубликовать пример о том, как решить эту проблему? – Chris

+0

Состояние экземпляра достаточно просто: оно должно быть NEW. Что-то должно организовывать события, чтобы изменить свое состояние. Обычно я называю это услугой, потому что он реализует конкретный прецедент. Он будет владеть экземпляром репозитория, который он будет использовать для создания нового билета или чтения существующего, изменения его состояния для удовлетворения прецедента, сохранения нового состояния как отдельной единицы работы и прекращения использования дело. – duffymo

1

Этот ответ, надеюсь, следует из duffymo's.

В представлении DDD мира ваш TicketState является сущностью, которая является частью агрегата Ticket (где билет является агрегированным корнем).

В соответствии с этим ваш TicketRepository имеет дело как с билетами, так и с TicketStates.

Когда вы извлекаете билет из слоя сохранения, вы затем позволяете TicketRepository извлекать состояние из БД и правильно устанавливать его в билете.

Если вы новичок в билете, то (я думаю) вам не нужно прикасаться к базе данных. Когда билет в конечном итоге сохраняется, вы берете новое состояние билета и сохраняете его правильно.

Ваши классы домена не должны знать ничего о модели базы данных, которая заботится о состоянии, им нужно знать только о состоянии модели домена. Тогда ваш репозиторий отвечает за это сопоставление.

+0

Но что, если я создаю новый билет, который еще не сохранен и вы хотите отобразить его имя состояния (хранящееся в БД) в пользовательском интерфейсе? Откуда мне получить эту информацию? – Chris

+0

@Chris - см. Мой комментарий ниже. – duffymo

0

Для меня простая модель пары ключ-значение, которая представляет состояние в базе данных (или какой-либо другой носитель), не нуждается в моделировании как таковой в домене. В домене я бы сделал TicketState перечислением и не несет ответственность за ITITETRepository, чтобы знать, как сопоставить это с требованиями схемы базы данных.

В репозитории билетов вы можете иметь кэш идентификаторов состояния билета, введенных в TicketState, который лениво загружается в статическую переменную (только один подход) из базы данных. Репозиторий билетов сопоставляет значение Ticket.State с идентификаторами из этого кеша для вставок/обновлений.

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; 
    } 
} 
+0

Поскольку модель стоит, я согласен с этим - однако, если переход состояния должен развиваться, чтобы он мог выражать бизнес-логику, то наличие субъекта государства может иметь большой смысл. –

+0

Но что, если я создаю новый билет, который еще не сохранен и вы хотите отобразить его имя состояния (хранящееся в БД) в пользовательском интерфейсе? Откуда мне получить эту информацию? – Chris

+0

Упростите его после того, как вы его создадите и, прежде чем отображать пользовательский интерфейс, конечно. Почему вы должны показать состояние постоянного объекта до его сохранения? Вы не показываете результат какой-либо операции пользователю до тех пор, пока он не будет завершен, и он не будет завершен, пока он не будет сохранен. – duffymo

Смежные вопросы