当前位置: 代码网 > it编程>软件设计>架构设计 > 领域驱动设计之银行转账:Wow 框架实战

领域驱动设计之银行转账:Wow 框架实战

2024年08月04日 架构设计 我要评论
银行账户转账案例是一个经典的领域驱动设计(DDD)应用场景。 接下来我们通过一个简单的银行账户转账案例,来了解如何使用 Wow 进行领域驱动设计以及服务开发。 银行转账流程 准备转账(Prepare): 用户发起转账请求,触发 Prepare 步骤。这个步骤会向源账户发送准备转账的请求。 校验余额(CheckBalance): 源账户在收到准备转账请求后,会执行校验余额的操作,确保账户有足够的余额进行转账。 锁定金额(LockAmount): 如果...

银行账户转账案例是一个经典的领域驱动设计(ddd)应用场景。

接下来我们通过一个简单的银行账户转账案例,来了解如何使用 wow 进行领域驱动设计以及服务开发。

银行转账流程

  1. 准备转账(prepare): 用户发起转账请求,触发 prepare 步骤。这个步骤会向源账户发送准备转账的请求。
  2. 校验余额(checkbalance): 源账户在收到准备转账请求后,会执行校验余额的操作,确保账户有足够的余额进行转账。
  3. 锁定金额(lockamount): 如果余额足够,源账户会锁定转账金额,防止其他操作干扰。
  4. 入账(entry): 接着,转账流程进入到目标账户,执行入账操作。
  5. 确认转账(confirm): 如果入账成功,确认转账;否则,执行解锁金额操作。
    1. 成功路径(success): 如果一切顺利,完成转账流程。
    2. 失败路径(fail): 如果入账失败,执行解锁金额操作,并处理失败情况。

saga-transfer

运行案例

自动生成 api 端点

saga-transfer

模块划分

模块 说明
example-transfer-api api 层,定义聚合命令(command)、领域事件(domain event)以及查询视图模型(query view model),这个模块充当了各个模块之间通信的“发布语言”。
example-transfer-domain 领域层,包含聚合根和业务约束的实现。聚合根:领域模型的入口点,负责协调领域对象的操作。业务约束:包括验证规则、领域事件的处理等。
example-transfer-server 宿主服务,应用程序的启动点。负责整合其他模块,并提供应用程序的入口。涉及配置依赖项、连接数据库、启动 api 服务

领域建模

账户聚合根

状态聚合根(accountstate)与命令聚合根(account)分离设计保证了在执行命令过程中,不会修改状态聚合根的状态。

状态聚合根(accountstate)建模

public class accountstate implements identifier {
    private final string id;
    private string name;
    /**
     * 余额
     */
    private long balanceamount = 0l;
    /**
     * 已锁定金额
     */
    private long lockedamount = 0l;
    /**
     * 账号已冻结标记
     */
    private boolean frozen = false;

    @jsoncreator
    public accountstate(@jsonproperty("id") string id) {
        this.id = id;
    }

    @notnull
    @override
    public string getid() {
        return id;
    }

    public string getname() {
        return name;
    }

    public long getbalanceamount() {
        return balanceamount;
    }

    public long getlockedamount() {
        return lockedamount;
    }

    public boolean isfrozen() {
        return frozen;
    }

    void onsourcing(accountcreated accountcreated) {
        this.name = accountcreated.name();
        this.balanceamount = accountcreated.balance();
    }

    void onsourcing(amountlocked amountlocked) {
        balanceamount = balanceamount - amountlocked.amount();
        lockedamount = lockedamount + amountlocked.amount();
    }

    void onsourcing(amountentered amountentered) {
        balanceamount = balanceamount + amountentered.amount();
    }

    void onsourcing(confirmed confirmed) {
        lockedamount = lockedamount - confirmed.amount();
    }

    void onsourcing(amountunlocked amountunlocked) {
        lockedamount = lockedamount - amountunlocked.amount();
        balanceamount = balanceamount + amountunlocked.amount();
    }

    void onsourcing(accountfrozen accountfrozen) {
        this.frozen = true;
    }

}

命令聚合根(account)建模

@statictenantid
@aggregateroot
public class account {
    private final accountstate state;

    public account(accountstate state) {
        this.state = state;
    }

    accountcreated oncommand(createaccount createaccount) {
        return new accountcreated(createaccount.name(), createaccount.balance());
    }

    @oncommand(returns = {amountlocked.class, prepared.class})
    list<?> oncommand(prepare prepare) {
        checkbalance(prepare.amount());
        return list.of(new amountlocked(prepare.amount()), new prepared(prepare.to(), prepare.amount()));
    }

    private void checkbalance(long amount) {
        if (state.isfrozen()) {
            throw new illegalstateexception("账号已冻结无法转账.");
        }
        if (state.getbalanceamount() < amount) {
            throw new illegalstateexception("账号余额不足.");
        }
    }

    object oncommand(entry entry) {
        if (state.isfrozen()) {
            return new entryfailed(entry.sourceid(), entry.amount());
        }
        return new amountentered(entry.sourceid(), entry.amount());
    }

    confirmed oncommand(confirm confirm) {
        return new confirmed(confirm.amount());
    }

    amountunlocked oncommand(unlockamount unlockamount) {
        return new amountunlocked(unlockamount.amount());
    }

    accountfrozen oncommand(freezeaccount freezeaccount) {
        return new accountfrozen(freezeaccount.reason());
    }
}

转账流程管理器(transfersaga

转账流程管理器(transfersaga)负责协调处理转账的事件,并生成相应的命令。

  • onevent(prepared): 订阅转账已准备就绪事件(prepared),并生成入账命令(entry)。
  • onevent(amountentered): 订阅转账已入账事件(amountentered),并生成确认转账命令(confirm)。
  • onevent(entryfailed): 订阅转账入账失败事件(entryfailed),并生成解锁金额命令(unlockamount)。
@statelesssaga
public class transfersaga {

    entry onevent(prepared prepared, aggregateid aggregateid) {
        return new entry(prepared.to(), aggregateid.getid(), prepared.amount());
    }

    confirm onevent(amountentered amountentered) {
        return new confirm(amountentered.sourceid(), amountentered.amount());
    }

    unlockamount onevent(entryfailed entryfailed) {
        return new unlockamount(entryfailed.sourceid(), entryfailed.amount());
    }
}

单元测试

internal class accountktest {
    @test
    fun createaccount() {
        aggregateverifier<account, accountstate>()
            .given()
            .`when`(createaccount("name", 100))
            .expecteventtype(accountcreated::class.java)
            .expectstate {
                assertthat(it.name, equalto("name"))
                assertthat(it.balanceamount, equalto(100))
            }
            .verify()
    }
}
(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com