标签: domain-driven-design

35
推荐指数
2
解决办法
5743
查看次数

CQRS中命令处理程序,聚合,存储库和事件存储之间的关系

我想了解基于CQRS的系统中命令处理程序,聚合,存储库和事件存储之间关系的一些细节.

到目前为止我所理解的:

  • 命令处理程序从总线接收命令.他们负责从存储库加载适当的聚合并调用聚合上的域逻辑.完成后,他们从总线上删除命令.
  • 聚合提供行为和内部状态.国家永远不公开.改变状态的唯一方法是使用行为.模拟此行为的方法从命令的属性创建事件,并将这些事件应用于聚合,聚合又调用事件处理程序,相应地设置内部状态.
  • 存储库只允许在给定ID上加载聚合,并添加新聚合.基本上,存储库将域连接到事件存储.
  • 事件存储,最后但并非最不重要,负责将事件存储到数据库(或任何使用的存储),并将这些事件重新加载为所谓的事件流.

到现在为止还挺好.现在有一些我还没有得到的问题:

  • 如果命令处理程序要在现有聚合上调用行为,那么一切都很简单.命令处理程序获取对存储库的引用,调用其loadById方法并返回聚合.但是,当没有聚合时,命令处理程序会做什么,但是应该创建一个?根据我的理解,聚合应该稍后使用事件重建.这意味着聚合的创建是在回复fooCreated事件时完成的.但是为了能够存储任何事件(包括fooCreated),我需要一个聚合.所以这对我来说就像鸡蛋和鸡蛋一样:我不能在没有事件的情况下创建聚合,但是应该创建事件的唯一组件是聚合.所以基本上归结为:我如何创建新的聚合,谁做了什么?
  • 当聚合触发事件时,内部事件处理程序会响应它(通常通过应用方法调用)并更改聚合的状态.这个事件如何移交给存储库?谁发起了"请将新事件发送到存储库/事件存储"动作?聚合本身?通过观察聚合来存储库?还有谁订阅了内部活动?...?
  • 最后但并非最不重要的是,我有一个问题正确理解事件流的概念:在我的想象中,它只是一个有序的事件列表.重要的是它是"有序的".这是正确的吗?

events domain-driven-design cqrs

35
推荐指数
2
解决办法
6388
查看次数

DDD,Anti Corruption层,操作方法?

目前,我们必须构建一个基于遗留应用程序的应用程序.旧应用程序的代码应该被丢弃并重写,但通常情况下 - 而不是重写它,我们需要在它上面添加新内容.最近,我们决定采用DomainDrivenDesign路径.所以 - 反腐败层可以解决我们的问题.据我所知,这种方式应该可以逐步重写旧的应用程序.

但是 - 我找不到任何好的例子.我很感激任何信息.

architecture legacy rewrite domain-driven-design

34
推荐指数
2
解决办法
2万
查看次数

存储库如何与CQRS相匹配?

根据Fowler(此处),存储库"在域和数据映射层之间进行调解,就像内存域对象集合一样." 因此,例如,在我的Courier Service应用程序中,当提交新的运行时,我的应用程序服务会创建一个新的运行聚合根对象,使用请求中的值填充它,然后将其添加到RunRepository,然后再调用要保存的工作单元对数据库的更改.当用户想要查看当前运行的列表时,我查询相同的存储库并返回表示该信息的非规范化DTO.

但是,在查看CQRS时,查询不会访问同一个存储库.相反,它可能直接针对数据存储并始终是非规范化的.我的命令方将演变为NewRunCommand和Handler,它将创建并填充NewRun域对象,然后将信息保存到数据存储.

所以第一个问题是,如果我们不维护域对象的内存中集合(缓存,如果你愿意的话),那么存储库在哪里适用于CQRS模型?

考虑提交给我的应用程序服务的信息只包含服务必须解析以构建域对象的一系列ID值的情况.例如,请求包含分配给运行的信使的ID#.服务必须根据ID值查找实际的Courier对象,并使用AssignCourier方法(验证信使并执行其他业务逻辑)将对象分配给NewRun.

另一个问题是,鉴于查询分离和可能缺少存储库,应用程序服务如何执行查找以查找Courier域对象?

UPDATE

根据丹尼斯评论后的一些额外阅读和思考,我将重新解释我的问题.

在我看来,CQRS鼓励仅仅是数据访问和数据存储机制的外观的存储库.它们给出了一个集合的"外观"(如Fowler描述的那样),但并没有在内存中管理实体(正如Dennis指出的那样).这意味着存储库上的每个操作都是直通的,是吗?

工作单位如何适应这种方法?通常,UoW用于提交对存储库所做的更改(对吗?),但如果存储库没有将实体维护在内存中,那么UoW有什么作用?

关于"写入"操作,命令处理程序是否会引用相同的存储库,不同的存储库或者可能是UoW而不是存储库?

domain-driven-design ddd-repositories cqrs

34
推荐指数
1
解决办法
2万
查看次数

DDD中的值对象 - 为什么不可变?

我不明白为什么DDD中的值对象应该是不可变的,我也不知道如何轻松完成.(如果重要的话,我专注于C#和实体框架.)

例如,让我们考虑经典的Address值对象.如果你需要改变"解放路123号"到"123主 ",我为什么要需要构建一个全新的对象,而不是说myCustomer.Address.AddressLine1 ="123大街"的?(即使实体框架支持结构,这仍然是一个问题,不是吗?)

我理解(我认为)价值对象没有身份并且是域对象的一部分的想法,但有人可以解释为什么不变性是一件好事吗?


编辑:我在这里的最后一个问题应该是"有人可以解释为什么不变性是适用于价值对象的好东西吗?" 对困惑感到抱歉!


编辑:为了clairfy,我不是在询问CLR值类型(与引用类型相比).我问的是价值对象的更高级DDD概念.

例如,这里是实现不可变的值类型实体框架劈上下的方式: http://rogeralsing.com/2009/05/21/entity-framework-4-immutable-value-objects.基本上,他只是让所有的安装者都私密.为什么要经历这样做的麻烦?

domain-driven-design value-objects

33
推荐指数
4
解决办法
1万
查看次数

为什么在服务和dao层中总是有单个实现接口?

我已经/看过一些spring-hibernate Web应用程序项目,它们具有与实际服务和dao类一样多的接口.

我一直认为这两个是这些单一实现接口的主要原因:

  1. Spring可以将实际实现作为给定类中的依赖项连接(松散耦合)

    public class Person { 
        @Autowired 
        private Address address;
    
        @Autowired 
        private AccountDetail accountDetail;
    
        public Person(Address address, AccountDetail accountDetail) 
        { // constructor
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在进行单元测试时,我可以创建模拟类并单独测试类.

    Address mockedAddress = mock(Address);
    AccountDetail mockedAccountDetail = mock(AccountDetail);
    Person underTestPerson = new Person(mockedAddress, mockedAccountDetail); 
    // unit test follows
    
    Run Code Online (Sandbox Code Playgroud)

但是,最近,我意识到:

Spring可以将具体实现类连接为依赖项:

public class Person { 

@Autowired 
private AddressImpl address;

@Autowired 
private AccountDetailImpl accountDetail;

public Person(AddressImpl address, AccountDetailImpl accountDetail) { 
// constructor
Run Code Online (Sandbox Code Playgroud)

像EasyMock这样的模拟框架也可以模拟具体的类

AddressImpl mockedAddress = mock(AddressImpl);
AccountDetailImpl mockedAccountDetail = mock(AccountDetailImpl);
Person underTestPerson = new Person(mockedAddress, mockedAccountDetail); …
Run Code Online (Sandbox Code Playgroud)

java spring design-patterns domain-driven-design mocking

33
推荐指数
2
解决办法
7088
查看次数

Node.js应用程序中的域驱动设计

TL; DR; 我正在寻找DDD node.js应用程序的一些例子.


嗨,

我要创建节点应用程序.我想知道我找不到任何在域中分离业务逻辑的应用程序示例.

好的,有一些例子:https: //github.com/adrai/node-cqrs-domain - 但这是整个CQRS与事件采购实现.

我的想法是那样做:

//domain/book.js
function Book(title, author)
{
  this._title = title;
  this._author = author;
}

// domain methods ...

//infrastructure/persistance/repository/book-repository.js
function BookRepository()
{}

BookRepository.prototype.save(book)
{
  var bookModel = mappers.mapToOrm(book);
  return bookModel.save();
}

// [...] get, getAll, getNextId

//infrastructure/persistance/orm/book.js
//using http://bookshelfjs.org/
var Book = bookshelf.Model.extend({
  tableName: 'books'
});

//infrastructure/mappers/book-mapper.js
function mapToOrm(book) {
  //mapping [...]
  return new persistance.Book();
}

function mapToDomain(domain) {
  //mapping [...]
  return new domain.Book();
}
Run Code Online (Sandbox Code Playgroud)

但另一方面,我从未见过任何类似的解决方案(使用域模型,orm模型,存储库和映射器).我是以正确的方式思考的吗?也许没有理由在node.js应用程序中分离域中的业务逻辑.如果是这样,为什么?如果没有,你能给我一个DDD实现的例子或改进我的代码吗?

[2017年1月13日]

我在TypeScript中创建了示例应用程序.目前没有存储库,没有太多服务.欢迎提出问题和拉取请求. https://github.com/dawiddominiak/ddd-typescript-bin-packing-problem-solution

javascript domain-driven-design node.js

33
推荐指数
2
解决办法
1万
查看次数

什么属于聚合根

这是一个实用的领域驱动设计问题:

从概念上讲,我认为在获得定义之前我会得到Aggregate.

我有一个Employee实体,它已表现为聚合根.在业务中,一些员工可以针对他们记录与工作相关的违规行为:

员工-----*违规

由于并非所有员工都受此限制,我认为违规行为不会成为员工总数的一部分,对吗?

因此,当我想与Employees及其相关的违规行为合作时,某个服务是否有两个独立的Repository交互?

最后,当我添加一个Violation时,是Employee Entity上的那个方法吗?谢谢您的帮助!

entities domain-driven-design aggregate root

32
推荐指数
2
解决办法
7249
查看次数

在哪里提出依赖于持久性的域事件 - 服务,存储库或UI?

我的ASP.NET MVC3/NHibernate应用程序需要触发和处理与我的域对象相关的各种事件.例如,Order对象可能包含OrderStatusChanged或等事件NoteCreatedForOrder.在大多数情况下,这些事件会导致发送电子邮件,因此我不能将它们留在MVC应用程序中.

我已经阅读了Udi Dahan的Domain Events以及关于如何做这类事情的许多其他想法,我决定使用一个处理事件消息的基于NServiceBus的主机.我做了一些概念验证测试,这似乎运作良好.

我的问题是应用程序层应该实际引发事件.我不想在有问题的对象成功保留之前触发事件(如果持久性失败,则无法发送创建注释的电子邮件).

另一个问题是,在某些情况下,事件与聚合根下面的对象相关联.在上面的示例中,Note通过将a 添加到Order.Notes集合并保存订单来保存a .这带来了一个问题,因为它很难评估Order保存时应该触发哪些事件.我想避免在保存更新的副本之前必须拉出对象的当前副本并查找差异.

  • 用户界面提出这些事件是否合适?它知道发生了什么事件,并且只有在成功使服务层保存对象后才能触发它们.让控制器触发域事件似乎有些不对劲.

  • 存储库是否应该在成功持久后触发事件?

  • 我是否应该完全分离事件,让存储库存储一个Event对象,然后由轮询器服务接收该对象,然后将其转换为NServiceBus的事件(或直接从轮询器服务处理)?

  • 有一个更好的方法吗?也许让我的域对象排队只有在持久化对象后由服务层触发的事件?

  • 更新:我有一个服务层,但让它通过比较过程以确定在保存给定聚合根时应该触发哪些事件似乎很麻烦和过分.由于其中一些事件是粒状的(例如"订单状态已更改"),我认为我必须检索对象的数据库副本,比较属性以创建事件,保存新对象,然后将事件发送到NServiceBus保存操作成功完成.

更新

在我下面发布的答案(下面的方法)之后,我最终做的是在我的域实体中构建一个EventQueue属性List<IDomainEvent>.然后我添加了事件,因为对域的更改是值得的,这使得我可以将逻辑保留在域中,我认为这是合适的,因为我根据实体内部的内容触发事件.

然后,当我将对象保留在服务层中时,我处理该队列并实际将事件发送到服务总线.最初,我计划使用使用身份PK的遗留数据库,因此我必须对这些事件进行后处理以填充实体的ID,但我最终决定切换到Guid.Comb允许我跳过该步骤的PK.

asp.net-mvc domain-driven-design nservicebus domain-events asp.net-mvc-3

32
推荐指数
3
解决办法
5724
查看次数

关于将实体映射到域对象的建议

我目前正在开展一个项目,我们开始使用DDD方法构建应用程序.我们现在首先考虑使用Entity Framework 6代码来帮助我们处理数据持久性.我的问题是如何最好地处理我们的域对象和EF实体之间的数据映射?

c# domain-driven-design entity-framework

32
推荐指数
3
解决办法
2万
查看次数