上一篇文章中,我們已經談完 event storming 的整個流程後,接下來我們就來看看如何轉成程式碼。
這個範例我們用 event storming 跑了一個情境,那就是會員機制,它的幾個需求為 :
然後根劇 event storming 的結如下 :
其中我們在 Member BC 有一個 Policy 為當收到已付款時,我們需要計算要不要升級會員。
先前提說明一下,這個比較算是範例碼,很多的細節事實上都沒有提到,但是如果要知道從 event storming 轉成程式碼,那應該只要先這樣就好,接下來後面的文章才會 peak 每個地方。
這裡有幾個重點 :
*/
class MemberAggregate extends BaseAggregate {
private id: string;
private name: string;
private email: string;
private lineId: string;
private tasksMap: Map<
string,
ProfileTask | LoginTask | RecommendRegisterTask | LineBindTask
>;
private Level: Level;
private hasSendLevelUpGift: boolean;
private cumulativeAmount: number;
constructor() {
super();
}
/**
* 產生已完成 profile 的 domain event
*/
public editorProfile(name: string, email: string): void {
this.name = name;
this.email = email;
const task: ProfileTask = this.tasksMap.get(
Tasks.PROFILE_TASK,
);
task.complete(name, email);
this.addDomainEvent(DomainEvent.PROFILE_EDITOR, {});
}
public bindLineId(lineId: string): void {
this.lineId = lineId;
const task: LineBindTask = this.tasksMap.get(
Tasks.LINE_BIND_TASK,
);
task.complete(lineId);
this.addDomainEvent(DomainEvent.BIND_LINE_ID, {});
}
public settingStudentPlan(): void {
// ...
const task: StudyPlanTask = this.tasksMap.get(
Tasks.StudyPlanSetting,
);
task.complete();
this.addDomainEvent(DomainEvent.STARTED_STUDENT_PLAN, {});
}
private calculateMemberLevel(newOrderAmount: number, notifyService: NofifyService): void {
if(cumulativeAmount + newOrderAmount > 1000){
this.Level = Level.L2
await notifyService.send('level up gift');
this.hasSendLevelUpGift = true;
this.addDomainEvent(DomainEvent.MEMBER_LEVEL_UP, {});
}
cumulativeAmount += newOrderAmount;
}
}
然後有幾個重點可以記一下:
class MemberService {
constructor(
private memberRepository: MemberRepository,
private notifyService: NofifyService
) {}
@SubscribeDomainEvent('Purchase:Order:Paid')
async receiveOrderPaid(order: Order): Promise<void> {
const member = await this.memberRepository.findMemberById(memberId);
await member.calculateMemberLevel(order.amount, notifyService);
await this.memberRepository.save(member);
await this.domainEventPublisher.publish(member.domainEvents);
}
備註: 這個資料夾結構我自已是覺得 DDD 的最簡單與理解 Bounded Context的版本,後面會說個考慮其它架構的版本。
然後下面上面的 dal 不要看,看 module 裡面,然後其中 member 與 purchase 就是我範例中說明 bounded context,然後這有以下幾個重點 :
然後這裡是建議不要每一次切個 BC 就開一個微服務,因為 BC 是會需要迭代改變的,如果太早切那個 BC 遲早就會變成 legacy。
~補充 Bounded Context 的設計重點~
這個我不確定,但以我的認知是要可以。
這個我自已覺得可以提現在 service 根據不同 actor 來進行切分,而至於要不要切 aggregate 好像也不是說不行。
之後 CQRS 的章節會提到。
這篇文章中,我們探討了如何將 Event Storming 的結果轉換為程式碼,但實務上還有一些難題還要處理 :
我自已覺得如果只是要實作 event storming 到程式碼中,不能說到很難,我覺得真正難的是後面如何整到流程,並且還有文件的變動如何管理才叫難事。