我实际上是CQRS和事件来源的新手。
当我看到以下代码时,我被命令处理程序弄得一头雾水:
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Aggregate
public class BankAccountAggregate {
@AggregateIdentifier
private UUID id;
private BigDecimal balance;
private String owner;
@CommandHandler
public BankAccountAggregate(CreateAccountCommand command){
AggregateLifecycle.apply(
new AccountCreatedEvent(
command.getAccountId(),
command.getInitialBalance(),
command.getOwner()
)
);
}
@Slf4j
@RequiredArgsConstructor
@Component
public class BankAccountProjection {
private final BankAccountRepository repository;
private final QueryUpdateEmitter updateEmitter;
@EventHandler
public void on(AccountCreatedEvent event) throws Exception {
log.debug("Handling a Bank Account creation command {}", event.getId());
BankAccount bankAccount = new BankAccount(
event.getId(),
event.getOwner(),
event.getInitialBalance()
);
this.repository.save(bankAccount);
Boolean isActive = AggregateLifecycle.isLive();
}
}
那么我们为什么不把帐户创建
的逻辑放在命令处理程序中呢?这样做的目的是什么?
完整的源代码在这个链接。
你在混合一些概念。让我们把它们弄清楚。
如果你实现了事件来源,这意味着你的真相来源是事件本身。您存储的是事件,而不是具体的“状态”(实体)。让我们看看一些伪代码:
创建新帐户:
function createAccount(data) {
event = new AccountCreatedEvent(data)
eventStore.save(event)
}
function withdraw(data) {
events = eventStore.getEvents(data.accountId)
account = new Account()
account.apply(events)
account.withdraw(data)
newEvents = account.newEvents
eventStore.save(newEvents)
}
class Account {
amount = 0
newEvents = []
function apply(events) {
events.forEach(event => {
if event == AccountCreatedEvent {
this.amount = event.initialAmount
} else if (event == WithdrawalApplied) {
this.amount = this.amount - event.amount
} // more event types
})
}
function withdraw(data) {
// here is where you ensure aggregate invariants (business rules)
if this.amount == 0 {
throw Error("no money")
}
this.amount = this.amount - data.amount
newEvents.add(new WithdrawalApplied(data))
}
}
该命令处理程序只是发布一个事件。正如您所看到的,对象的命名是AccountCreatedEvent,但这并不意味着帐户已经创建,对吗?
答案是您应该将事件存储在命令处理程序中。这恰恰意味着账户被创建了。然后,如果需要,您可以在正在进行的过程中发布事件。继续读。
使用CQRS,您只需分离查询和命令,但这种技术可以在根本不需要事件源的情况下应用。
下图说明了三种技术(CQRS、事件来源和预测)是如何协同工作的。
主要内容:一、Sql流程,二、源码分析,四、总结一、Sql流程 MySql是数据库,这次就分析一下一条SQL语句的流程,流程可能不会全面展开分析,当后面遇到具体的模块时,再由各个模块深入学习。如果使用过MySql的客户端(任意一种都可以),基本的形式就是在客户端写一条SQL语句,然后点击运行,正常的情况下,就会返回这条SQL执行后的结果。 可能学习Sql源码的人不少,但学习编译器知识的人就少多了。在SQL语句的执行过程中,其实SQL语句就是一门
问题内容: 在Redis 4.0中,有一个新命令UNLINK删除Redis内存中的密钥。 此命令与DEL非常相似:它将删除指定的密钥。与DEL一样,如果密钥不存在,则将其忽略。但是,该 命令在不同的线程中执行实际的内存回收 , 因此当DEL为时 , 它不会阻塞 。这就是命令名称的来源:该命令只是将键与键空间断开链接。实际的删除将在以后异步发生。 因此,可以始终(100%次)使用UNLINK而不是D
在本章中,我们来看一看一些经常使用的批处理命令。如下图中所示 - 编号 命令 描述 1 VER 此批处理命令显示正在使用的MS-DOS的版本。 2 ASSOC 这是将扩展名与文件类型(FTYPE)相关联的批处理命令,显示现有关联或删除关联。 3 CD 此批处理命令用于更改进入不同的目录,或显示当前目录。 4 CLS 这个批处理命令清除屏幕。 5 COPY 此批处理命令用于将文件从一个位置复制到另一
#,## # 和 ## 操作符是和#define宏使用的. 使用# 使在#后的首个参数返回为一个带引号的字符串. 例如, 命令 #define to_string( s ) # s 将会使编译器把以下命令 cout << to_string( Hello World! ) << endl; 理解为 cout << "Hello World!" << endl;
预处理命令 #, ## manupilate字符串 #define 定义变量 #error 显示一个错误消息 #if, #ifdef, #ifndef, #else, #elif, #endif 条件操作符 #include 插入其它文件的内容 #line 设置行和文件信息 #pragma 执行特殊命令 #undef 取消定义变量 预定义变量 其它变量
主要内容:实例前面各章中,已经多次使用过 命令。使用库函数之前,应该用 引入对应的头文件。 这种以号开头的命令称为预处理命令。 C语言源文件要经过编译、链接才能生成可执行程序: 1) 编译(Compile)会将源文件( 文件)转换为目标文件。对于 VC/VS,目标文件后缀为 ;对于 GCC,目标文件后缀为 。 编译是针对单个源文件的,一次编译操作只能编译一个源文件,如果程序中有多个源文件,就需要多次编译操作。