1 回答
TA贡献1847条经验 获得超7个赞
你的选项 #1 对我来说看起来最好,但有一些额外的观察(注意:我会坚持你的命名,这会使其中一些看起来有点矫枉过正......再次重要的是想法)。
而不是
Entity
我会说Realm
代表一个AggregateRoot
.这可以是隐式的,也可以内联一个
base.AggregateRoot
.聚合根是域的访问点,并确保其状态始终一致。
因此内部状态
Realm
应该是不可变的。状态变化通过函数发生。除非真的微不足道并且不太可能改变,否则我会
FriendlyName
在单独的文件中实现值对象。域的一部分也是 a ,
RealmRepository
但这仅提供一个接口。
现在我正在使用 CQRS,它是对您的代码片段中显示的内容的扩展。在这个:
ChangeFriendlyName
应用层中可能有一个命令处理程序。处理程序可以访问存储库实现,例如
InMemRealmRepository
。可能会将 a 传递
CreateRealmParams
给命令,然后命令进行验证。Realm
处理程序逻辑可能从从数据库中获取聚合开始。然后构造一个新的
FriendlyName
(也可以封装在一个Realm
函数调用中)。Realm
更新状态和排队事件的函数调用FriendlyNameChanged
。命令处理程序将更改保存到 InMemory 数据库。
只有在没有错误的情况下,命令处理程序才会
Commit()
在聚合上调用。一个或多个排队的事件现在发布,例如通过
EventBus
,在需要的地方处理。
至于选项 #1 的代码有些变化(希望我做对了)..
realm.go - 聚合根
type Realm struct {
base.AggregateRoot
friendlyName FriendlyName
}
// Change state via function calls. Not shown: event impl, error handling.
// Even with CQRS having Events is entirely optional. You might implement
// it solely to e.g. maintain an audit log.
func (r *Realm) ChangeFriendlyName(name FriendlyName) {
r.friendlyName = name
var ev = NewFriendlyNameChanged(r.id, name)
// Queue the event.
r.Apply(ev)
}
// You might use Params types and encapsulate value object creation,
// but I'll pass value objects directly created in a command handler.
func CreateRealm(id base.AID, name FriendlyName) (*Realm, error) {
ar := base.NewAggregateRoot(id)
// Might do some param validation here.
return &Realm{
AggregateRoot: ar,
friendlyName: name,
}, nil
}
friendlyname.go - 值对象
type FriendlyName struct {
value string
}
// Domain error. Part of ubiquitous language.
var FriendlyNameInvalid = errors.New("invalid friendly name")
func (n FriendlyName) String() string { return n.value }
func NewFriendlyName(input string) (FriendlyName, error) {
if input == "" {
return FriendlyNameInvalid
}
// perhaps some regexp rule here...
return FriendlyName{value: input}, nil
}
- 1 回答
- 0 关注
- 148 浏览
添加回答
举报