-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b7da557
commit 256bc71
Showing
21 changed files
with
452 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Day 26 - transactional outbox | ||
|
||
Today's task is to implement a pattern that sends out events and persist aggregate changes in single transaction. | ||
Usually it is implemented on database with a table dedicated for events, where they are stored. | ||
Events are picked up from this table and sent by another thread. | ||
|
||
The most important for this pattern to work is to persist changes and events in the same transaction. | ||
For task clearness purposes, tests for transaction checks are skipped, but feel free to write them. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
plugins { | ||
id 'org.springframework.boot' version '3.0.1' | ||
id 'io.spring.dependency-management' version '1.1.0' | ||
id 'java' | ||
} | ||
|
||
group 'com.pawelpluta' | ||
version '0.0.1' | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
runtimeOnly 'org.postgresql:postgresql' | ||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' | ||
testImplementation 'org.springframework.boot:spring-boot-starter-test' | ||
testImplementation 'org.testcontainers:testcontainers:1.17.6' | ||
testImplementation 'org.testcontainers:postgresql:1.17.6' | ||
testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3' | ||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.1' | ||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.1' | ||
} | ||
|
||
test { | ||
useJUnitPlatform() | ||
} | ||
|
||
java { | ||
toolchain { | ||
languageVersion.set(JavaLanguageVersion.of(19)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
rootProject.name = 'day024' |
99 changes: 99 additions & 0 deletions
99
day026/src/main/java/com/pawelpluta/day026/BankAccount.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package com.pawelpluta.day026; | ||
|
||
import java.math.BigDecimal; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.UUID; | ||
|
||
public class BankAccount { | ||
|
||
private final String accountNumber; | ||
private final BigDecimal balance; | ||
private final List<Event> events; | ||
|
||
private BankAccount(String accountNumber, BigDecimal balance, List<Event> events) { | ||
this.accountNumber = accountNumber; | ||
this.balance = balance; | ||
this.events = events; | ||
} | ||
|
||
public String accountNumber() { | ||
return accountNumber; | ||
} | ||
|
||
public BigDecimal balance() { | ||
return balance; | ||
} | ||
|
||
public List<Event> events() { | ||
return events; | ||
} | ||
|
||
public static BankAccount of(String accountNumber, BigDecimal balance) { | ||
return new BankAccount(accountNumber, balance, Collections.emptyList()); | ||
} | ||
BankAccount handle(ChargeAccountCommand command) { | ||
if (balance.compareTo(command.amount()) >= 0) { | ||
BigDecimal updatedBalance = balance.subtract(command.amount()); | ||
return new BankAccount( | ||
accountNumber, | ||
updatedBalance, | ||
List.of(new BankAccountBalanceDecreased(accountNumber, balance, updatedBalance))); | ||
} else { | ||
return new BankAccount( | ||
accountNumber, | ||
balance, | ||
List.of(new AccountChargeRejectedDueToInsufficientFounds(accountNumber, balance, command.amount()))); | ||
} | ||
} | ||
|
||
static class BankAccountBalanceDecreased implements Event { | ||
|
||
private final String id; | ||
private final String accountNumber; | ||
private final BigDecimal oldBalance; | ||
private final BigDecimal newBalance; | ||
|
||
BankAccountBalanceDecreased(String accountNumber, BigDecimal oldBalance, BigDecimal newBalance) { | ||
id = UUID.randomUUID().toString(); | ||
this.accountNumber = accountNumber; | ||
this.oldBalance = oldBalance; | ||
this.newBalance = newBalance; | ||
} | ||
|
||
@Override | ||
public String id() { | ||
return id; | ||
} | ||
|
||
@Override | ||
public String jsonBody() { | ||
return "{}"; | ||
} | ||
} | ||
|
||
static class AccountChargeRejectedDueToInsufficientFounds implements Event { | ||
|
||
private final String id; | ||
private final String accountNumber; | ||
private final BigDecimal balance; | ||
private final BigDecimal charge; | ||
|
||
AccountChargeRejectedDueToInsufficientFounds(String accountNumber, BigDecimal balance, BigDecimal charge) { | ||
id = UUID.randomUUID().toString(); | ||
this.accountNumber = accountNumber; | ||
this.balance = balance; | ||
this.charge = charge; | ||
} | ||
|
||
@Override | ||
public String id() { | ||
return id; | ||
} | ||
|
||
@Override | ||
public String jsonBody() { | ||
return "{}"; | ||
} | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
day026/src/main/java/com/pawelpluta/day026/BankAccountRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.pawelpluta.day026; | ||
|
||
import java.util.Optional; | ||
|
||
public interface BankAccountRepository { | ||
Optional<BankAccount> findById(String id); | ||
void save(BankAccount account); | ||
} |
6 changes: 6 additions & 0 deletions
6
day026/src/main/java/com/pawelpluta/day026/ChargeAccountCommand.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.pawelpluta.day026; | ||
|
||
import java.math.BigDecimal; | ||
|
||
record ChargeAccountCommand(String accountId, BigDecimal amount) { | ||
} |
18 changes: 18 additions & 0 deletions
18
day026/src/main/java/com/pawelpluta/day026/CommandHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.pawelpluta.day026; | ||
|
||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
class CommandHandler { | ||
|
||
private final BankAccountRepository accountRepository; | ||
|
||
CommandHandler(BankAccountRepository accountRepository) { | ||
this.accountRepository = accountRepository; | ||
} | ||
|
||
void handle(ChargeAccountCommand command) { | ||
accountRepository.findById(command.accountId()) | ||
.ifPresent(bankAccount -> accountRepository.save(bankAccount.handle(command))); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.pawelpluta.day026; | ||
|
||
public interface Event { | ||
String id(); | ||
String jsonBody(); | ||
|
||
record EventVO(String id, String jsonBody) implements Event {} | ||
} |
7 changes: 7 additions & 0 deletions
7
day026/src/main/java/com/pawelpluta/day026/EventSupplier.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.pawelpluta.day026; | ||
|
||
import java.util.List; | ||
|
||
public interface EventSupplier { | ||
List<Event> fetchEventsToSend(); | ||
} |
12 changes: 12 additions & 0 deletions
12
day026/src/main/java/com/pawelpluta/day026/TransactionalOutboxApp.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.pawelpluta.day026; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
|
||
@SpringBootApplication | ||
class TransactionalOutboxApp { | ||
|
||
public static void main(String[] args) { | ||
SpringApplication.run(TransactionalOutboxApp.class, args); | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
day026/src/main/java/com/pawelpluta/day026/jpa/BankAccountEntity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.pawelpluta.day026.jpa; | ||
|
||
import com.pawelpluta.day026.BankAccount; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.Table; | ||
|
||
import java.math.BigDecimal; | ||
|
||
@Entity | ||
@Table(name = "accounts") | ||
class BankAccountEntity { | ||
@Id | ||
@Column(name = "account_id") | ||
private String id; | ||
@Column(name = "balance") | ||
private BigDecimal balance; | ||
|
||
public BankAccountEntity() { | ||
} | ||
|
||
public BankAccountEntity(String id, BigDecimal balance) { | ||
this.id = id; | ||
this.balance = balance; | ||
} | ||
|
||
static BankAccountEntity from(BankAccount account) { | ||
return new BankAccountEntity( | ||
account.accountNumber(), | ||
account.balance()); | ||
} | ||
|
||
BankAccount toModel() { | ||
return BankAccount.of(id, balance); | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
day026/src/main/java/com/pawelpluta/day026/jpa/EventEntity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package com.pawelpluta.day026.jpa; | ||
|
||
import com.pawelpluta.day026.Event; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.Table; | ||
|
||
@Entity | ||
@Table(name = "events_outbox") | ||
class EventEntity { | ||
@Id | ||
@Column(name = "event_id") | ||
private String id; | ||
@Column(name = "body") | ||
private String body; | ||
@Column(name = "sent") | ||
private Boolean sent; | ||
|
||
public EventEntity() { | ||
} | ||
|
||
public EventEntity(String id, String body) { | ||
this.id = id; | ||
this.body = body; | ||
sent = false; | ||
} | ||
|
||
static EventEntity from(Event event) { | ||
return new EventEntity( | ||
event.id(), | ||
event.jsonBody()); | ||
} | ||
|
||
String getId() { | ||
return id; | ||
} | ||
|
||
String getBody() { | ||
return body; | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
day026/src/main/java/com/pawelpluta/day026/jpa/JpaBankAccountEntityRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.pawelpluta.day026.jpa; | ||
|
||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
interface JpaBankAccountEntityRepository extends JpaRepository<BankAccountEntity, String> { | ||
} |
27 changes: 27 additions & 0 deletions
27
day026/src/main/java/com/pawelpluta/day026/jpa/JpaBankAccountRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.pawelpluta.day026.jpa; | ||
|
||
import com.pawelpluta.day026.BankAccount; | ||
import com.pawelpluta.day026.BankAccountRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
import java.util.Optional; | ||
|
||
@Repository | ||
class JpaBankAccountRepository implements BankAccountRepository { | ||
|
||
private final JpaBankAccountEntityRepository repository; | ||
|
||
JpaBankAccountRepository(JpaBankAccountEntityRepository repository) { | ||
this.repository = repository; | ||
} | ||
|
||
@Override | ||
public Optional<BankAccount> findById(String id) { | ||
return repository.findById(id).map(BankAccountEntity::toModel); | ||
} | ||
|
||
@Override | ||
public void save(BankAccount account) { | ||
repository.save(BankAccountEntity.from(account)); | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
day026/src/main/java/com/pawelpluta/day026/jpa/JpaEventEntityRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.pawelpluta.day026.jpa; | ||
|
||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
import java.util.List; | ||
|
||
@Repository | ||
interface JpaEventEntityRepository extends JpaRepository<EventEntity, String> { | ||
|
||
List<EventEntity> findAllBySentIsFalse(); | ||
|
||
} |
22 changes: 22 additions & 0 deletions
22
day026/src/main/java/com/pawelpluta/day026/jpa/JpaEventPublisherRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.pawelpluta.day026.jpa; | ||
|
||
import com.pawelpluta.day026.Event; | ||
import com.pawelpluta.day026.EventSupplier; | ||
import org.springframework.stereotype.Repository; | ||
|
||
import java.util.List; | ||
|
||
@Repository | ||
class JpaEventPublisherRepository implements EventSupplier { | ||
|
||
private final JpaEventEntityRepository repository; | ||
|
||
JpaEventPublisherRepository(JpaEventEntityRepository repository) { | ||
this.repository = repository; | ||
} | ||
|
||
@Override | ||
public List<Event> fetchEventsToSend() { | ||
return repository.findAllBySentIsFalse().stream().map(entity -> (Event) new Event.EventVO(entity.getId(), entity.getBody())).toList(); | ||
} | ||
} |
Oops, something went wrong.