域驱动设计的一部分似乎没有太多细节,是您应该如何以及为什么要将域模型与界面隔离开来.我试图说服我的同事,这是一个很好的做法,但我似乎没有取得多大进展......
他们在演示文稿和界面层中随意使用域实体.当我向他们争辩说他们应该使用显示模型或DTO来将Domain层与接口层隔离时,他们反驳说他们在做类似的事情时看不到业务价值,因为现在你有一个UI对象要维护以及原始域对象.
所以我正在寻找一些可以用来支持它的具体原因.特别:
architecture design-patterns domain-driven-design data-transfer-objects presentation-layer
我目前拥有数据库中几乎每个表的存储库,并希望通过将它们简化为仅聚合根来进一步使自己与DDD保持一致.
我们假设我有以下表格,User并且Phone.每个用户可能有一部或多部电话.如果没有聚合根的概念,我可能会这样做:
//assuming I have the userId in session for example and I want to update a phone number
List<Phone> phones = PhoneRepository.GetPhoneNumberByUserId(userId);
phones[0].Number = “911”;
PhoneRepository.Update(phones[0]);
Run Code Online (Sandbox Code Playgroud)
聚合根的概念在纸上比在实践中更容易理解.我永远不会有不属于用户的电话号码,所以取消PhoneRepository并将电话相关的方法合并到UserRepository中是否有意义?假设答案是肯定的,我将重写先前的代码示例.
我是否允许在UserRepository上有一个返回电话号码的方法?或者它应该始终返回对用户的引用,然后通过用户遍历关系以获取电话号码:
List<Phone> phones = UserRepository.GetPhoneNumbers(userId);
// Or
User user = UserRepository.GetUserWithPhoneNumbers(userId); //this method will join to Phone
Run Code Online (Sandbox Code Playgroud)
无论我采用哪种方式获取手机,假设我修改了其中一种,我该如何更新它们?我有限的理解是,根目录下的对象应该通过root更新,这将引导我选择下面的#1.尽管这对于Entity Framework非常有效,但这似乎非常缺乏描述性,因为读取代码时我不知道我实际更新了什么,即使实体框架在图形中保留了更改对象的选项卡.
UserRepository.Update(user);
// Or
UserRepository.UpdatePhone(phone);
Run Code Online (Sandbox Code Playgroud)
最后,假设我有几个查阅表中并没有真正依赖于任何东西,如CountryCodes,ColorsCodes,SomethingElseCodes.我可能会用它们来填充下拉或其他任何原因.这些独立的存储库吗?它们可以组合成某种逻辑分组/存储库,例如CodesRepository?或者是针对最佳做法的.
c# asp.net domain-driven-design entity-framework entity-framework-4
我觉得他们基本上都是一样的.模型对象也一样吗?
现在,在我的架构中,我有:
class Person
{
public string PersonId;
public string Name;
public string Email;
public static bool IsValidName() { /* logic here */ }
public static bool IsValidEmail() { /* logic here */ }
}
class PersonService
{
private PersonRepository pRepository;
PersonService()
{
pRepository = new PersonRepository();
}
public bool IsExistingEmail(string email)
{
//calls repo method to see if email is in db
}
public Person GetPerson(email)
{
return pRepository.Get(email);
}
public void SavePerson(Person p)
{
if (Person.IsValidEmail(p.Email) && !IsExistingEmail(p.Email) …Run Code Online (Sandbox Code Playgroud) 我们来看一个简单的"帐户注册"示例,这里是流程:
当然,我们可以通过读取MVC控制器中的ReadModel来验证UserName的唯一性,以提高性能和用户体验.但是,我们仍然需要在RegisterCommand中再次验证唯一性,显然,我们不应该在命令中访问ReadModel.
如果我们不使用Event Sourcing,我们可以查询域模型,这样就没问题了.但是如果我们使用Event Sourcing,我们无法查询域模型,那么我们如何验证RegisterCommand中的UserName唯一性?
注意: User类具有Id属性,UserName不是User类的键属性.在使用事件源时,我们只能通过Id获取域对象.
顺便说一句:在要求中,如果已经输入了UserName,则网站应向访问者显示错误消息"抱歉,用户名XXX不可用".显示一条消息是不可接受的,例如"我们正在创建您的帐户,请稍后,我们会通过电子邮件将注册结果发送给您"给访问者.
有任何想法吗?非常感谢!
[UPDATE]
一个更复杂的例子:
需求:
下订单时,系统应该检查客户的订购历史,如果他是一个有价值的客户(如果客户在去年每月至少订购10个订单,他是有价值的),我们会在订单上减价10%.
执行:
我们创建PlaceOrderCommand,在命令中,我们需要查询订购历史记录以查看客户端是否有价值.但是我们怎么能这样做呢?我们不应该在命令中访问ReadModel!正如Mikael 所说,我们可以在帐户注册示例中使用补偿命令,但如果我们在此订购示例中也使用补偿命令,那么它将太复杂,并且代码可能难以维护.
我是DDD的新手,我正试图在现实生活中应用它.没有关于这种验证逻辑的问题,如空检查,空字符串检查等 - 直接进入实体构造函数/属性.但是在哪里验证一些全局规则,如"唯一用户名"?
所以,我们有实体用户
public class User : IAggregateRoot
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
// other data and behavior
}
Run Code Online (Sandbox Code Playgroud)
和用户存储库
public interface IUserRepository : IRepository<User>
{
User FindByName(string name);
}
Run Code Online (Sandbox Code Playgroud)
选项包括:
每个选项更详细:
1.将存储库注入实体
我可以在实体构造函数/属性中查询存储库.但我认为在实体中保持对存储库的引用是一种难闻的气味.
public User(IUserRepository repository)
{
_repository = repository;
}
public string Name
{
get { return _name; }
set
{
if (_repository.FindByName(value) != null)
throw new UserAlreadyExistsException(); …Run Code Online (Sandbox Code Playgroud) 您如何处理域驱动设计中复杂聚合的验证?您是否整合了业务规则/验证逻辑?
我理解参数验证.我理解可以附加到模型本身的属性验证,并执行检查电子邮件地址或邮政编码是否有效或者名字具有最小和最大长度等操作.
但是涉及多个模型的复杂验证呢?您通常将这些规则和方法放在您的架构中?你用什么模式来实现它们?
我读过Eric Evan的书,现在正在阅读Vaughn Vernon的书.我在第二章中讨论了子域和有界的背景,现在我已经彻底混淆了.
从我能够提炼出来的情况来看,BC和SD之间应该存在1:1的关系.但是,我在其他地方读到这不一定是这种情况.
有人可以向我解释BC和SD之间的关系吗?
我曾经看过一些书(例如编程实体框架代码Julia Lerman)定义了他们的域类(POCO)而没有初始化导航属性,如:
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<Address> Address { get; set; }
public virtual License License { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
生成POCO时,其他一些书籍或工具(例如Entity Framework Power Tools)初始化类的导航属性,如:
public class User
{
public User()
{
this.Addresses = new IList<Address>();
this.License = new License();
}
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<Address> Addresses { …Run Code Online (Sandbox Code Playgroud) c# domain-driven-design entity-framework navigation-properties ef-code-first
我还没有看到任何示例,但我认为它们保存在数据库中的包含实体表中.
IE浏览器.如果我有一个Person实体/聚合根和一个相应的Person表,如果它有一个名为Address的值对象,则Address值将保存在此Person表中!
对于我有其他实体(如公司等)拥有地址的域名,这是否有意义?
(我目前正在编写项目管理应用程序并尝试进入DDD)