DDD в сложном корпоративном приложении MVC, какова ответственность хранилища и что принадлежит домену? - PullRequest
4 голосов
/ 14 февраля 2012

Я работаю над сложным корпоративным приложением. Большая часть этого - обмен информацией и управление данными различными способами.

Я использую шаблон Repository для сохранения данных, но в итоге получаю то, что считаю нелогичным или полностью анемичным доменом.

Ядром приложения являются пользователи (в данном случае сотрудники). У меня есть конкретный пример моей проблемы при реализации функциональности для пользователей, чтобы отправлять сообщения:

  1. друг друга
  2. Для менеджеров для отображения сообщения «Must read» с важной информацией

Для последнего у меня есть это (немного упрощенно):

    public class Message
{
    public int Id { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidTo { get; set; }
    public string Body { get; set; }
    public Employee CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }

    public ICollection<Employee> Recipients { get; set; }

    public void Send(Message message)
    {
        //Send the message
        //This is persisting it in the database and update the table
        //that maps to the recipients
        //WHO SENDS IT, THE USER?
    }

}

public class User
{
    public int Id { get; set; }
    public String Name { get; set; }
    public Department Department { get; set; }
    public ICollection<Message> Messages { get; set; }

    public Message Recieve(Message message)
    {
        //Mark the message as recieved in DB
        return message;
    }

    public void Read (Message message)
    {
        //Mark the message as read in DB
    }

    public void Delete (Message message)
    {
        //Have to query the database to see if this user can delete
        //this message
        //If so, delete the message in DB
    }

    //THE USER ALSO THEN
    //Creates messages
    //Makes orders
    //Changes passwords and adress
    //Checks deliveries
    //Corrects delivered amounts
    //Manages an department
    //Archives documents
    //Writes new documents
    //AND A LOT MORE
}

Как вы видите, большинство операций - это запись информации в БД или необходимость запроса БД для получения информации для принятия правильных решений. В корпоративных приложениях, над которыми я работал, большинство операций связаны с управлением данными, хранением настроек и информации о пользователях, а также с записью, кто что сделал и когда в базе данных.

Большая часть этого подходит наиболее естественному в хранилище. Должен ли я позвонить в хранилище из вышеперечисленных классов и попросить сохранить информацию? У меня такое чувство, что нарушает правила. Является ли удаление сообщения поведением вообще? Если нет, то какое поведение имеет домен, ориентированный на данные?

Что делает пользователь , а не ? Мне трудно обдумать это, то есть сообщение - это, в основном, данные, поэтому в реальной жизни оно ничего не может сделать. Заказ не может создать сам (это тоже просто часть информации), пользователь должен сделать это и так далее. Написание файлов, обмен данными через xml, сервисы, pdf или что-то еще - это просто разные способы обмена и / или сохранения данных?

Я знаю, что должно быть какое-то объяснение этой концепции, но, потратив часы на ее чтение, мне трудно найти реальное значение DDD, и примеры, которые я видел, слишком далеки от реальной проблемы, где у вас есть проверить историю или получить информацию из БД, чтобы принять правильные решения. Это больше похоже на философское упражнение, чем на логику ...

Если вы можете прочитать разочарование между строк, я просто хочу отметить, что это касается не кого-то, кроме меня самого, так как я не могу понять, как реализовать функциональность сообщения и все же сделать это по книге.

Редактировать

На основании приведенного ниже вопроса я вижу, что мне нужно добавить немного больше информации:

Я использую EF Code First, поэтому моя модель предметной области и модель персистентности действительно совпадают (насколько я понимаю), поскольку я не делал никаких специальных сопоставлений. Пользователь должен получить сообщения при входе в систему, чтобы отобразить их, пометить как прочитанные и, если он / она хочет, заархивировать их (не отображается в списке сообщений).

В качестве моего первоначального вопроса у меня нет проблем с размещением всего необходимого кода в репозитории (получение сообщений, добавление нового сообщения, изменение сообщения, пометка сообщения как прочитанного и т. Д.), Но это также моя проблема, что тогда следует делать зайти в модель домена?

Приложение представляет собой веб-приложение MVC, которое не имеет состояния. Относится ли DDD к веб-приложению без сохранения состояния в его традиционном смысле?

Мне нравится идея, лежащая в основе этого, и я думал о том, чтобы делать так.

Добавьте новый класс, который будет выполнять функцию архива, сделайте сообщение совокупным корнем с архивом как дочерним:

    public class MessageArchive
    {
        public Message Message { get; set; }
        public User Recipient { get; set; }
        public User From { get; set; }
        private bool Read { get; set; }
        private bool Archived { get; set; }

    public void MarkMessageAsRead()
    {
        //Do things - i.e. notify sender that the message is read
        Read = true;
    }

    public void ArchiveMessage()
    {
        //Do things to archive the message, in this case, mark it as archived
        Archived = true;
    }

    }


    public class Message
{
    public int Id { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidTo { get; set; }
    public string Body { get; set; }
    public User CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public virtual ICollection<MessageArchive> ArchivedMessages { get; set; } 

    public void Send(IEnumerable<User> recipients, User from)
    {
        foreach (var recipient in recipients)
        {
            ArchivedMessages.Add(new MessageArchive
                                     {
                                         From = from, 
                                         Recipient = recipient, 
                                         Message = this,
                                     });
        }
    }

}

При создании и отправке сообщения оно будет:

    public void Run()
    {
        var message = new Message();
        var recipients = new List<User>();
        //Fill it with recipients
        message.Send(recipients, currentUser);
        _unitOfWork.MessageRepository.Add(message);
        _unitOfWork.Save();

    }

Неужели я здесь совсем не на том пути, должен ли я забыть о DDD и просто заставить его работать или это шаг к лучшему дизайну?

Ответы [ 2 ]

2 голосов
/ 14 февраля 2012

Во-первых, модель предметной области отличается от модели постоянства.При использовании OR \ M вы определяете модель постоянства, а не домен.При разработке модели предметной области вы начинаете моделировать потребности в использовании (при отсутствии содержательной формулировки) заинтересованных сторон (владельцев приложений и пользователей) и игнорируете все, что связано с постоянством, но когда вы разрабатываете модель постоянства, вы заботитесь о хранилище./ постоянство потребности.

Хранилище абстрагирует все, что связано с постоянством.Прикладной уровень знает только об интерфейсе репозитория, куда он отправляет доменные объекты или команды и откуда он возвращает доменные объекты или модели представления (на самом деле упрощенно уплощено для отображения представления модели).

DDD - это прежде всего образ мышления.Вы начинаете с того, что приложение должно решить, и вы представляете потребности, проблемы и решения в коде, используя самый естественный язык (вы видите, куда я стремлюсь?).Вам не важно, что и как будет сохранено, база данных не существует.Однако вы должны принять во внимание ограничения платформы и модели использования, т.е. проектировать объекты в соответствии с тем, как они используются и где используются.Нет никакого смысла в заполнении целого объекта Порядка, когда на самом деле вам нужен только идентификатор.Этот Порядок может быть объединенным корнем, но вы можете использовать интерфейсы, так что вы будете работать с его облегченной версией в зависимости от желаемой функциональности.

Если модель предметной области анемична, это означает, что у вас ОЧЕНЬ ПРОБНОЕ приложение, которого здесь нет.Это означает, что это хорошая идея для рефакторинга модели предметной области.

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

0 голосов
/ 15 февраля 2012

Я просто не мог оставить это в покое, но думаю, что после МНОГО работы у меня есть решение, которое соответствует философии DDD, так что вот что я закончил:

Первый интерфейс сообщения:

public interface IMessage
{
    int Id { get; set; }
    string Body { get; set; }
    string Title { get; set; }
    Employee CreatedBy { get; set; }
    MessageType MessageType { get; set; }
    ICollection<MessageArchive> ArchivedMessages { get; set; }
    void Send(IEnumerable<Employee> recipients);
}

Затем MessageArchive (это может быть объект персистентности, но я считаю его почтовым ящиком)

    public class MessageArchive
{
    public int Id { get; set; }
    public IMessage Message { get; set; }
    public Employee Recipient { get; set; }
    public Employee Sender { get; set; }
    private bool Read { get; set; }
    private bool Archived { get; set; }

    public void MarkMessageAsRead()
    {
        //Do things - i.e. notify sender that the message is read
        Read = true;
    }

    public void ArchiveMessage()
    {
        //Do things to archive the message, in this case just mark it as archived
        Archived = true;
    }

}

Я делаю PublicMessage только здесь (PrivateMessage также вызывает MailService - я думаю, что детали smtp и отправки почты принадлежат службе, а не домену):

 public class PublicMessage: IMessage
{
    public int Id { get; set; }
    public MessageType MessageType { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidTo { get; set; }
    public string Body { get; set; }
    public string Title { get; set; }
    public Employee CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public virtual ICollection<MessageArchive> ArchivedMessages { get; set; } 

    public void Send(IEnumerable<Employee> recipients)
    {
        var archive = new List<MessageArchive>();
        foreach (var recipient in recipients)
        {
            archive.Add(new MessageArchive
                                     {
                                         Sender = CreatedBy, 
                                         Recipient = recipient, 
                                         Message = this,
                                     });
        }

        ArchivedMessages = archive;
    }

}

Сообщение является Агрегированным Корнем, MessageArchive является дочерним. При использовании это значительно удобнее для чтения, чем установка всех значений через репозиторий, и намного проще добавлять события и функции позже.

        var message = new PublicMessage
                          {
                              //Create the message
                          };
        var recipients = new List<Employee>() {//Create a list of recipients};

        //We send the message
        message.Send(recipients);

        //Persisting it is just adding a message to through the repository and save
        unitOfWork.MessageRepository.Add(message);
        unitOfWork.Save();

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

Пожалуйста, прокомментируйте, если у вас есть какие-либо отзывы по этому поводу. В любом случае, я надеюсь, что это может помочь кому-то, кто проходит через тот же процесс, что и я ... Я практически мечтал о DDD в последние дни и действительно беспокоился о том, как правильно понять ...

Теперь проблема в том, как инфраструктуре сущностей нравятся интерфейсы, наследование и т. Д. Я уже сталкивался там с некоторыми проблемами. Трудно запустить DDD, когда нужно принять во внимание требования ORM, но я думаю, это просто опыт.

...