Hello Word
在开始之前,我们假定您已经具备了一些基本技能:
- 熟悉 Java 环境配置及其开发。最低支持 JDK17
- 有一些项目开发经验及了解一些 DDD 的相关概念
- 熟悉 Java 构建工具,比如 Maven
现在我们用一个很常见的简单用户模块来熟悉领域驱动的开发模式。
用户模块需求
这个需求非常简单:用户含用户名(唯一)、年龄。
- 用户注册(创建):用户名必填且需唯一,年龄默认 18。
- 用户名修改年龄。
- 用户销毁:删除用户。
引入框架
- Maven
<dependency>
<groupId>net.qiqbframework</groupId>
<artifactId>qiqb-core</artifactId>
<version>${lastVersion}</version>
</dependency>
领域建模
根据需求构建出一个用户领域模型(聚合根)及相关业务方法入口。
import lombok.Getter;
import net.qiqbframework.modelling.domain.AggregateContext;
import net.qiqbframework.modelling.domain.AggregateRoot;
import net.qiqbframework.modelling.domain.AggregateRootId;
import java.io.Serializable;
@AggregateRoot
@Getter
public class User implements Serializable {
/**
* 用户名,唯一
*/
@AggregateRootId
protected String name;
/**
* 年龄
*/
protected int age;
/**
* 创建用户
*
* @param name
* @param age
*/
public User(String name, int age) {
this.name = name;
this.age = age;
if (this.age == 0) {
this.age = 18;
}
verifyAge();
}
/**
* 修改年龄
*
* @param newAge 新的年龄
*/
public void changeAge(int newAge) {
this.age = newAge;
verifyAge();
}
/**
* 验证年龄是否合法
*/
private void verifyAge() {
if (this.age < 0 || this.age > 150) {
throw new IllegalArgumentException("age must be between 0 and 150");
}
}
/**
* 注销
*/
public void unregister() {
AggregateContext.markDeleted();
}
}
领域仓库
仓库具有两个功能:加载和持久化。加载是会一次性加载领域的所有数据,保存则是一次性保存领域的所有数据。
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.qiqbframework.loadhanding.LoadHandler;
import net.qiqbframework.persisthanding.PersistHandler;
import net.qiqbframework.persisthanding.PersistType;
import net.qiqbframework.user.domain.User;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class UserRepository {
@Getter
public final Map<String, User> DB = new HashMap<>();
/**
* 根据 用户聚合根 Id 加载用户聚合根对象
*
* @param name
* @return
*/
@LoadHandler
public User loadByName(String name) {
return DB.get(name);
}
/**
* 保存用户聚合根对象
*
* @param user
*/
@PersistHandler(type = PersistType.SAVE)
public void save(User user) {
DB.put(user.getName(), user);
}
/**
* 删除用户
*
* @param user
*/
@PersistHandler(type = PersistType.REMOVE)
public void remove(User user) {
DB.remove(user.getName());
}
}
接下来就是通过命令来调用聚合根对象。
用户注册命令
import lombok.Setter;
import net.qiqbframework.modelling.command.Cmd;
import net.qiqbframework.modelling.command.FetchHandler;
import net.qiqbframework.user.domain.User;
@Cmd(User.class)// 定义这个命令需要操作的聚会根
public class UserRegisterCmd {
private final String name;
@Setter
private int age = 18;
public UserRegisterCmd(String name) {
this.name = name;
}
@FetchHandler // 获取聚会根
public User create() {
return new User(name, age);
}
}
测试验证
通过命令网关CommandGateway发送命令进行测试。但是获取命令网关前需要做一些初始化一下配置组件。
本文中所有测试测试采用了 jupiter 测试,以下代码会在本章节后面重复使用。
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import net.qiqbframework.commandhandling.gateway.CommandGateway;
import net.qiqbframework.config.Configuration;
import net.qiqbframework.config.Configurer;
import net.qiqbframework.config.ConsoleBannerModule;
import net.qiqbframework.config.DefaultConfigurer;
public class UserTest {
CommandGateway commandGateway;
UserRepository userPersist;
@BeforeEach
void setUp() {
System.out.println("setUp");
// 配置中心
Configurer configurer = DefaultConfigurer.defaultConfiguration()
.registerModule(new ConsoleBannerModule());
// 注册聚合根定义
configurer.aggregateConfigurer().registerAggregateRootTypeBuilder(c -> User.class);
// 用户仓库
userPersist = new UserRepository();
// 注册用户加载器
configurer.loadConfigurer().registerLoadHandlerBuilder(c -> userPersist);
// 注册用户持久化
configurer.persistenceConfigurer().registerPersistenceHandlerBuilder(c -> userPersist);
// 注册命令类型
configurer.commandConfigurer().registerAggregateCommandType(c -> UserRegisterCmd.class);
// 编译不过可先注释,等创建 UserAgeChangeCmd 类的时候注入即可
configurer.commandConfigurer().registerAggregateCommandType(c -> UserAgeChangeCmd.class);
// 编译不过可先注释,等创建 UserUnregisterCmd 类的时候注入即可
configurer.commandConfigurer().registerAggregateCommandType(c -> UserUnregisterCmd.class);
Configuration configuration = configurer.buildConfiguration();
configuration.start();
commandGateway = configuration.commandGateway();
}
}
这些繁琐的配置没必要担心,会在Spring 和 Quarkus 环境中会简化掉。
// ... existing code ...
@Test
public void registerUser() {
String userName = "foo";
UserRegisterCmd userRegisterCmd = new UserRegisterCmd(userName);
commandGateway.sendAndWait(userRegisterCmd);
assertEquals(userName, userPersist.DB.get(userName).getName());
}
// ... existing code ...
用户修改年龄命令
import net.qiqbframework.modelling.command.BizHandler;
import net.qiqbframework.modelling.command.BizIdentifierVoucher;
import net.qiqbframework.modelling.command.Cmd;
import net.qiqbframework.user.domain.User;
@Cmd(User.class)
public class UserAgeChangeCmd {
@BizIdentifierVoucher
private final String name;
private final int newAge;
public UserAgeChangeCmd(String name, int newAge) {
this.name = name;
this.newAge = newAge;
}
@BizHandler
public void handle(User user) {
user.changeAge(newAge);
}
}
测试验证
// ... existing code ...
@Test
public void changeAge() {
registerUser();
String userName = "foo";
UserAgeChangeCmd userAgeChangeCmd = new UserAgeChangeCmd(userName, 19);
commandGateway.sendAndWait(userAgeChangeCmd);
assertEquals(19, userPersist.DB.get(userName).getAge());
}
// ... existing code ...
用户销毁命令
import net.qiqbframework.modelling.command.BizHandler;
import net.qiqbframework.modelling.command.BizIdentifierVoucher;
import net.qiqbframework.modelling.command.Cmd;
import net.qiqbframework.user.domain.User;
@Cmd(User.class)
public class UserUnregisterCmd {
@BizIdentifierVoucher
private final String name;
public UserUnregisterCmd(String name) {
this.name = name;
}
@BizHandler
public void handle(User user) {
user.unregister();
}
}
测试验证
// ... existing code ...
@Test
public void unregisterUser() {
registerUser();
String userName = "foo";
UserUnregisterCmd userRegisterCmd = new UserUnregisterCmd(userName);
commandGateway.sendAndWait(userRegisterCmd);
assertEquals(0, userPersist.DB.size());
}
// ... existing code ...
小结一下
本章节通过简单功能模拟出增删改流程。但 Qiqb Framework 远不止这些功能,想要详细了解更多功能吗?那就继续往下看吧!我相信您会获取不一样的开发方式和乐趣。
enjoy fun!!!