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 關注
- 195 瀏覽
添加回答
舉報