我正在开发一个复杂的企业应用程序。其中很大一部分是以不同的方式共享信息和管理数据。
我使用存储库模式来保存数据,但最终会被我认为是一个不合逻辑或完全贫血的域。
应用程序的核心是用户(在本例中为Employees)。在实现用户向其发送消息的功能时,我有一个具体的问题:
对于后者,我有这个(稍微简化):
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并让它发挥作用或者这是朝着更好的设计迈出的一步?
答案 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,但这只是我想的经验。