DDD在复杂的企业MVC应用程序中,什么是存储库的责任以及属于域的什么?

时间:2012-02-13 23:20:45

标签: asp.net-mvc-3 oop domain-driven-design repository-pattern enterprise

我正在开发一个复杂的企业应用程序。其中很大一部分是以不同的方式共享信息和管理数据。

我使用存储库模式来保存数据,但最终会被我认为是一个不合逻辑或完全贫血的域。

应用程序的核心是用户(在本例中为Employees)。在实现用户向其发送消息的功能时,我有一个具体的问题:

  1. 彼此
  2. 让经理显示包含重要信息的“必读”讯息
  3. 对于后者,我有这个(稍微简化):

        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的真正含义,我看到的例子离你真正的问题太远了以某种方式检查历史记录或从DB获取信息以做出正确的决定。感觉更像是一种哲学运动而不是逻辑......

    如果你能阅读这些内容之间的挫败感,我只想指出它不是我自己以外的任何人,因为我似乎无法找到如何实现消息功能,而且仍然可以通过本书来实现。< / p>

    修改

    基于下面的问题,我看到我需要添加更多信息:

    我使用EF Code First,因此我的域模型和持久性模型实际上是相同的(据我所知),因为我没有做过任何特殊的映射。用户必须在登录时检索消息以显示它们,将其标记为已读,并且他/她想要将其存档(不显示在消息列表中)。

    作为我最初的问题,我没有问题将所有需要的代码放入存储库(获取消息,添加新消息,更改消息,将消息标记为已读等)但这也是我的问题,应该怎样进入域模型?

    该应用程序是一个无状态的MVC网络应用程序, DDD的思维方式是否属于传统意义上的无状态网络应用程序?

    我喜欢它背后的想法,并且正在考虑这样做。

    添加一个新类来提供Archive的功能,使该消息成为一个聚合根,并将该档案作为子项:

        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 个答案:

答案 0 :(得分:2)

首先,域模型与持久性模型不同。使用OR \ M时,您可以定义持久性模型,而不是域。在设计领域模型时,您开始模拟利益相关者(应用程序所有者和用户)的使用需求(缺乏有意义的措辞)并忽略与持久性相关的任何内容,但是当您设计持久性模型时,您关心的是存储/持久性需求。

Repository抽象出与持久性相关的所有内容。应用程序层只知道它发送域对象或命令的存储库接口,以及它从哪里获取域对象或视图模型(事实上,为了显示模型的表示,实际上简化了)。

DDD主要是一种心态。您从应用程序需要解决的问题开始,并使用最自然的语言表示代码中的需求,问题和解决方案(您能看到我的目标吗?)。您不关心它将保存什么或如何保存,数据库不存在。但是,您应该考虑平台限制和使用模式,即根据对象的使用方式和使用位置来设计对象。在一个整个Order对象中,没有任何意义,实际上你只需要一个id。该订单可能是聚合根,但您可以使用接口,以便根据所需的功能使用它的轻量级版本。

如果域模型贫乏,则意味着您拥有非常繁琐的应用程序,而这并非如此。这意味着重构域模型是个好主意。

有一点要说,一些代码示例会好得多,但很难在现场的简单答案中做到这一点。我希望至少我已经传达了一些对你有帮助的想法。

答案 1 :(得分:-1)

我不能单凭这一点,但是经过大量的工作后我认为我有一个符合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;
    }

}

消息是Aggregate Root,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,并且真的很担心如何进入正确的思维方式......

问题现在是实体框架如何喜欢接口和继承等,我已经遇到了一些问题。当你必须考虑到ORM的要求时很难启动DDD,但这只是我想的经验。