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



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

我使用Repository模式来持久化数据,但最终得到了我认为要么是不合逻辑的领域,要么是完全贫血的领域。

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

  1. 彼此
  2. 让经理显示包含重要信息的"必读"信息

对于后者,我有这个(有点简化):

    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获取信息才能做出正确的决定。这感觉更像是一种哲学练习,而不是逻辑。。。

如果你能读到字里行间的沮丧,我只想指出,这不在我自己身上,因为我似乎找不到如何实现消息功能,并且仍然按照书来做。

编辑

基于以下问题,我认为我需要添加更多信息:

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

作为我最初的问题,我可以将所有需要的代码放入存储库中(获取消息、添加新消息、更改消息、将消息标记为已读等),但这也是我的问题,然后域模型中应该包含什么?

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

不过,我喜欢它背后的想法,并想过这样做。

添加一个新的类来提供Archive的功能,使消息成为聚合根,并将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,让它发挥作用,还是这是朝着更好的设计迈出的一步?

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

Repository抽象了与持久性相关的所有内容。应用程序层只知道存储库接口,它在那里发送域对象或命令,从哪里返回域对象或视图模型(事实上,为了显示模型的表示形式而简单化了扁平化)。

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

如果域模型是贫血的,这意味着你有一个非常三重的应用程序,而这里的情况并非如此。这意味着重构域模型是个好主意。

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

我不能就此打住,但经过大量工作,我认为我有了一个符合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,真的很担心如何进入正确的思维方式。。。

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

最新更新