领域驱动设计 CQRS 与多个聚合和界限上下文

-1 投票
0 回答
20 浏览
提问于 2025-04-12 00:30

我正在尝试结合命令事件,以及领域驱动设计,但不太确定如何处理涉及多个聚合的命令。

假设我有一个使用场景,一个新客户选择了一个计划,这时需要为这个客户创建一个账户,并将客户添加为该账户的成员。同时,这个账户还需要加载通过购买所选计划获得的套餐详情,比如用户限制和总座位数。

为此,我定义了一个账户聚合,其中成员是账户上下文中的本地实体,计划聚合则在订阅上下文中包含套餐值对象。我会触发CreateAccountCommand,传入plan_id、customer_name和account_name,然后我需要从计划聚合中获取与plan_id相关的套餐详情,并用这些信息来创建新的账户和成员。

那么,处理这种情况的有效方法是什么呢?根据我所读到和理解的,我认为获取计划详情的过程应该分开处理,查询的结果应该传递给CreateUserCommand,以便执行相应的操作。但我不太确定如何做到这一点,以创建一个松耦合和灵活的设计。

#Account aggregate with Members - Account Context
class Account(AggregateRoot):
    plan_id: uuid
    name: str
    credit: Money
    total_seats = AllocatedTotal
    user_limit: AllocatedUsers
    members: list[Member]

    def __init__(self, name: str, plan_id: uuid):
        self.name = name
        self.plan_id = plan_id
        self.members = []
        self.user_limit = AllocatedUsers(0)
        self.total_seats = AllocatedTotal(0)
        self.credit = Money(0)

    def add_member(self, member_name: str) 
        pass

class Member(Entity):
    name: str
    role: Role

    #other member related stuffs

# Plan aggregate with Package - Subscription Context
class Plan(AggregateRoot):
    name: str
    type: str
    package: Package

class Package(ValueObject):
    total_seats: AllocatedTotal
    user_limit: AllocatedUsers

#command
@dataclass
class CreateAccount(Command):
    account_name: str
    user_name: str
    plan_id: uuid


#command_handler
class CreateAccountHandler(CommandHandler):
    def __init__(self, uow: AbstractUnitOfWork,
                 account_service: 'AccountService'):
        self.uow = uow
        self.account_service = account_service

    def handle(self, command: SubscribeUser) -> None:
        with self.uow:
            self.account_service.create_account(plan_id=command.plan_id, account_name=command.account_name,
                                                member_name=command.user_name)
            self.uow.commit()

    def __enter__(self):
        return self
    
#domain_service
class AccountService(AccountServicePort):
    def __init__(self, account_repository: AccountRepository,
                 plan_service: PlanService):
        self.account_repository = account_repository
        self.plan_service = plan_service

    def create_account(self, plan_id: UUID, account_name: str, member_name: str) -> None:
        plan = self.plan_service.get_plan_by_id(plan_id)
        if not plan:
            raise ValueError("Plan not found")

        account = Account(name=account_name, plan_id=plan_id)
        member = Member(name=member_name, role=Role.ADMIN)
        account.add_member(member.id, member)
        account.load_total_users(plan.package.user_limit.count)
        account.total_seats(plan.package.total_seats.total)
        self.account_repository.add(entity=account)

0 个回答

暂无回答

撰写回答