Slide 1

Slide 1 text

"*Y&4 "*ʹΑΔΠϕϯτετʔϛϯάਤ͔Βͷίʔυੜ੒ OST ੒੉Ҹએ

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

3 ։࠵ʂ ͓ΊͰͱ͏͍͟͝·͢ʂʂ

Slide 4

Slide 4 text

4 ͱ͜ΖͰ

Slide 5

Slide 5 text

5 ͜ͷ޿ࠂݟ·ͨ͠ʁ

Slide 6

Slide 6 text

6 ͜ͷ޿ࠂݟ·ͨ͠ʁ ࣮͸$234 &4͕͜͜ʹજΜͰ͍ΔͷͰ͢ʂʂ

Slide 7

Slide 7 text

7 9ͷ޿ࠂϚωʔδϟʔ ΠϕϯτιʔγϯάͬΆ͍

Slide 8

Slide 8 text

8 9ͷ޿ࠂϚωʔδϟʔ $234

Slide 9

Slide 9 text

9 ΍ͬͺ9ຽ͸$234 &4ͩΑͳʂʂ

Slide 10

Slide 10 text

ϓϩϙʔβϧ

Slide 11

Slide 11 text

Πϕϯτετʔϛϯά ਤͷ࣮૷ "*࣌୅ͷίʔσΟϯά "*ʹΑΔΠϕϯτετʔϛϯάਤ͔Βͷίʔυੜ੒

Slide 12

Slide 12 text

Πϕϯτετʔϛϯά ਤͷ࣮૷ "*࣌୅ͷίʔσΟϯά "*ʹΑΔΠϕϯτετʔϛϯάਤ͔Βͷίʔυੜ੒

Slide 13

Slide 13 text

̏ͭͷϑΣʔζ l ϏοάϐΫνϟʔ l શମ૾Λ೺Ѳ͢Δ l ϏδωεϓϩηεϞσϦϯά l ղ૾౓Λ্͛Δ l ιϑτ΢ΣΞγεςϜϞσϦϯά l ࣮૷ʹ޲͚ͨઃܭΛ͢Δ

Slide 14

Slide 14 text

ϏοάϐΫνϟʔ

Slide 15

Slide 15 text

ϏδωεϓϩηεϞσϦϯά

Slide 16

Slide 16 text

ιϑτ΢ΣΞγεςϜϞσϦϯά

Slide 17

Slide 17 text

 ̍ɽΠϕϯτΛ෇ᝦʹॻ͖ग़͢ ̎ɽΠϕϯτΛฒͼସ͑Δ ̏ɽίϚϯυ΍ू໿ɾ֎෦γεςϜΛ௥Ճ͢Δ ̐ɽϙϦγʔΛ௥Ճ͢Δ ̑ɽશମͷ֬ೝͱσΟεΧογϣϯ ͬ͘͟ΓϏδωεϓϩηεϞσϦϯά·Ͱͷखॱ

Slide 18

Slide 18 text

 ϏϧσΟϯάϒϩοΫ

Slide 19

Slide 19 text

19 ΞΫλʔ ίϚϯυʢޙड़ʣΛ ࣮ߦ͢Δओମ ΞΫλʔ͸ ίϚϯυͷ࣮ߦʹ ؔ͢Δ൑அ΋͢Δ

Slide 20

Slide 20 text

20 ίϚϯυ ԿΒ͔ͷΞΫγϣϯΛ ࣮ߦ͢Δҙਤ΍ཁٻ

Slide 21

Slide 21 text

21 ू໿ ίϚϯυΛॲཧ͢Δ ΦϒδΣΫτ

Slide 22

Slide 22 text

22 Πϕϯτ Ϗδωε্Ͱൃੜͨ͠ ॏཁͳग़དྷࣄ΍ࣄ࣮

Slide 23

Slide 23 text

23 ϦʔυϞσϧ σʔλͷಡΈऔΓʹ ࠷దԽ͞Εͨ σʔλߏ଄΍Ϗϡʔ

Slide 24

Slide 24 text

24 ϙϦγʔ ࣗಈԽ͞Εͨ ൑அϩδοΫ ৚݅Λ͍ΕΔͷ͸ OSTྲྀ

Slide 25

Slide 25 text

25 ϙϦγʔ ۭཝͷϙϦγʔ͸ ʮඞͣʯͷҙຯ ˞ϙϦγʔʹ໊લΛ͚ͭͯ΋ ͋·Γ໾ʹཱͨͳ͍ͷͰ ۭཝʹ͢Δͷ͸ OST ࣜ

Slide 26

Slide 26 text

26 ֎෦γεςϜ ର৅γεςϜͷ ڥք֎ʹ͋ΔγεςϜ

Slide 27

Slide 27 text

27 දهͷҙຯɿෳ਺ͷΠϕϯτ ݁Ռͷ෼ذ΍ ෳ਺ͷग़དྷࣄ͕ฒྻʹى͖Δ ͜ͱΛද͢

Slide 28

Slide 28 text

28 ● ෇ᝦ͸ಛఆͷ෇ᝦ͔Β͔͠ܨ͛ΒΕͳ͍ ϧʔϧ ࠨਤͷ໼ҹͷ ܨ͕ΓํҎ֎͸ /(

Slide 29

Slide 29 text

Πϕϯτετʔϛϯά ਤͷ࣮૷ "*࣌୅ͷίʔσΟϯά "*ʹΑΔΠϕϯτετʔϛϯάਤ͔Βͷίʔυੜ੒

Slide 30

Slide 30 text

30 ΞΫλʔ ΞΫλʔͱίϚϯυ͸ ͍͍ͨͯ $6%ͷ "1*ͱͳΔ ʢ8FCͷ৔߹ʣ

Slide 31

Slide 31 text

 @Operation(summary = "Creating a new doc.") @PostMapping @ResponseStatus(HttpStatus.CREATED) public DocsPostResponse post(@Validated @RequestBody DocsPostRequest request) { var command = new DocCreate(new DocId(), request.body()); DocId docId = commandGateway.sendAndWait(command); return new DocsPostResponse(docId.value()); }

Slide 32

Slide 32 text

 public record DocCreate(DocId docId, String body) implements DocCommand { }

Slide 33

Slide 33 text

33 ू໿ ίϚϯυΛॲཧ͢Δ ΦϒδΣΫτΛ ఆٛ͢Δ

Slide 34

Slide 34 text

 @Aggregate public class DocAggregate extends AbstractAggregate { @Override protected DocId getAggregateRootId(Doc aggregate) { if (aggregate != null) { return aggregate.docId(); } else { return null; } } public DocAggregate() { } @CommandHandler public DocAggregate(DocCreate command) { var event = Doc.create(command.docId(), command.body()); apply(event); } @CommandHandler public void handle(DocUpdate command) { apply(it -> it.update(command.body())); } }

Slide 35

Slide 35 text

 public record Doc( DocId docId, String body ) implements EventDrivenAggregateRoot { public static DocCreated create(DocId docId, String body) { return new DocCreated(docId, body); } public static Doc applyEvent(DocCreated event) { return new Doc(event.docId(), event.body()); } public DocUpdated update(String body) { return new DocUpdated(docId, body); } @Override public EventDrivenAggregateRoot applyEvent(DocEvent event) { return switch (event) { case DocCreated docCreated -> new Doc(docCreated.docId(), docCreated.body()); case DocUpdated docUpdated -> new Doc(this.docId, docUpdated.body()); default -> throw new IllegalStateException("Unexpected value: " + event); }; } }

Slide 36

Slide 36 text

36 Πϕϯτ ΠϕϯτΛ ΦϒδΣΫτͰ ఆٛ͢Δ

Slide 37

Slide 37 text

 public record DocCreated( DocId docId, String body ) implements DocEvent { }

Slide 38

Slide 38 text

38 ϦʔυϞσϧ 8FCͰσʔλͱͯ͠ ༻ҙ͢ΔͳΒ (FUͷ8FC"1*ͱ σʔλऔಘॲཧ

Slide 39

Slide 39 text

 @GetMapping("{docsId}") public DocsGetResponse get(@PathVariable UUID docsId) throws ExecutionException, InterruptedException { var query = new GetDoc(docsId); var result = queryGateway.query(query, GetDocResult.class).get(); var docDataModel = result.docDataModel().orElseThrow(NotFoundException::new); return new DocsGetResponse(docsId, docDataModel.getBody(), docDataModel.getEffectiveCount()); }

Slide 40

Slide 40 text

 @Service public record DocQueryService(QueryGateway queryGateway, DocRepository repository) { @QueryHandler public GetDocResult handle(GetDoc query) { return new GetDocResult(repository.findById(query.docId().toString())); } }

Slide 41

Slide 41 text

 @Repository public interface DocRepository extends JpaRepository { }

Slide 42

Slide 42 text

 @Component public class DocProjection { private final DocRepository repository; public DocProjection(DocRepository repository) { this.repository = repository; } @EventHandler public void on(DocCreated event) { var doc = new DocDataModel(); doc.setDocId(event.docId().asString()); doc.setBody(event.body()); repository.save(doc); } @EventHandler public void on(DocUpdated event) { var doc = repository.findById(event.docId().asString()).orElseThrow(); doc.setBody(event.body()); repository.save(doc); } : }

Slide 43

Slide 43 text

43 ϙϦγʔ ΠϕϯτϋϯυϥʔͰ ༻ҙ͞ΕΔ

Slide 44

Slide 44 text

 @EventHandler public void on(AlbumCreated event) { if (!event.photos.isEmpty()) { commandGateway.send(new AddPhotos(event.albumId, event.photos)) } }

Slide 45

Slide 45 text

45 ֎෦γεςϜ ίϚϯυϋϯυϥʔ ʴ ෗ഊ๷ࢭ૚

Slide 46

Slide 46 text

 @CommandHandler public void handle(MakePayment command) { var result = paymentService.makePayment(command.accountId(), command.amount()); if (result.isSuccessful()) { eventGateway.publish( new PaymentSucceeded(command.paymentId(), result.getTransactionId())); } else { eventGateway.publish( new PaymentFailed(command.paymentId(), result.getFailureReason())); } }

Slide 47

Slide 47 text

Πϕϯτετʔϛϯά ਤͷ࣮૷ "*࣌୅ͷίʔσΟϯά "*ʹΑΔΠϕϯτετʔϛϯάਤ͔Βͷίʔυੜ੒

Slide 48

Slide 48 text

48 "*࢖ͬͯͳౕ͍͍Δʁ

Slide 49

Slide 49 text

49 ͍Ͷ͐Αͳ͊ʂʂʁ

Slide 50

Slide 50 text

 "*ͷಛੑʢ͍·͞Β͕ͩʜʜʣ

Slide 51

Slide 51 text

51 ίϯςΩετ΢Οϯυ΢໰୊ $POUFYU3PU -PTUJOUIF.JEEMF ສτʔΫϯೖΔ ʺ ສτʔΫϯΛཧղ͢Δ

Slide 52

Slide 52 text

52 l ܇࿅σʔλʹͳ͍৘ใΛٻΊΒΕͨͱ͖ l ࠷৽"1* l ࣾ಺ن໿ l ͍͋·͍ͳࢦࣔΛड͚ͨͱ͖ l ʮ͍͍ײ͡ʹͯ͠ʯ l จ຺͕ෆ଍͍ͯ͠Δͱ͖ l ෳ਺ͷղऍ͕Մೳͳ࣭໰ ϋϧγωʔγϣϯΛى͜͠΍͍͢ঢ়گ

Slide 53

Slide 53 text

 ͓હཱͯɿίϯςΩετ΢Οϯυ΢ฤ

Slide 54

Slide 54 text

54 ߏ଄Խ͸͍͍ͧʂ ߏ଄Ͱऔࣺબ୒͕Ͱ͖Δ

Slide 55

Slide 55 text

55 7FSUJDBM4MJDF ਫฏϨΠϠʔ͸ ෳ਺σΟϨΫτϦΛ ԣஅ͠΍͍͢ͷͰ ਨ௚ʹ෼ׂ͢Δͷ͕ ΑΓ(PPE

Slide 56

Slide 56 text

 ͓હཱͯɿϋϧγωʔγϣϯฤ

Slide 57

Slide 57 text

57 ৘ใΛ౉͢ ● ܇࿅σʔλʹͳ͍΋ͷΛҾ͖౉͢ #### 2.1. Set-Based Consistency Validationύλʔϯʹ͓͚ΔίϚϯυ෼཭ ू໿࡞੒લʹ֎෦σʔλʢϦϙδτϦ౳ʣΛࢀরͨ͠ݕূ͕ඞཁͳ৔߹ɺίϚϯυΛ2ͭʹ෼཭͢Δɿ **֎෦ެ։ίϚϯυʢRequest* Commandʣ**: - **໨త**: ΫϥΠΞϯτ͔ΒͷϦΫΤετΛड͚औΓɺݕূΛ࣮ࢪ - **໋໊نଇ**: `Request{໊ࢺ}Command` (ྫ: `RequestReservationSlotCommand`) - **Ξϊςʔγϣϯ**: `@RoutingKey` - Application ServiceʹϧʔςΟϯά - **ϋϯυϥʔ**: Application Serviceʢ`@Service` + `@CommandHandler`ʣͰॲཧ - **ྫ**: ```java public record RequestReservationSlotCommand( @RoutingKey String slotId, String facilityId // ... ͦͷଞͷϑΟʔϧυ ) {} ```

Slide 58

Slide 58 text

58 બ୒ࢶΛ௵͢ ʮΠϕϯτΛ࡞ͬͯʯ͚ͩͩͱ ໋໊͸ 0SEFS$SFBUFE 0SEFS$SFBUFE&WFOU $SFBUFE0SEFS Ͳ͜ʹஔ͘ʁ FWFOUT EPNBJOFWFOUT BQJ ● ͍͋·͍ͳࢦࣔΛݻఆ͢Δ - **໋໊نଇ**: `{໊ࢺ}{ಈࢺաڈ෼ࢺ}Event` (ྫ: `FacilityRegisteredEvent`, `ReservationCancelledEvent`) - **ྫ**: ʮอҭࢪઃ͕ొ࿥͞Εͨʯ→ `FacilityRegisteredEvent` - **ύοέʔδ**: `com.example.eventsourcing.{context}.api.events`

Slide 59

Slide 59 text

59 ϧʔϧΛఆٛ ● จ຺͕ෆ଍͍ͯ͠Δͱ͜ΖΛϧʔϧͰΧόʔ ### 1. ΠϕϯτʢΦϨϯδ৭ͷ෇ᝦʣ - **ఆٛ**: γεςϜͰൃੜͨ͠ࣄ࣮ΛաڈܗͰදݱ - **࣮૷**: ֤Bounded Contextͷ`api/events`ύοέʔδʹrecordͱͯ͠ఆٛ - **഑ஔཧ༝**: ଞͷBounded Context΍ϚΠΫϩαʔϏε͔ΒࢀরՄೳͳެ։API ### σΟϨΫτϦߏ଄ ``` src/main/java/com/example/eventsourcing/ ├── {bounded-context}/ # ྫ: facility, parent │ ├── api/ │ │ └── events/ # ⭐ ެ։ΠϕϯτʢଞαʔϏε͔ΒࢀরՄೳʣ │ │ └── {Context}Events.java │ ├── adapter/ │ │ ├── inbound/ # Inbound Adaptersʢೖྗʣ

Slide 60

Slide 60 text

 ͕Μ͹Γ͗͢ͳ͍

Slide 61

Slide 61 text

61 ● ϓϩϯϓτ࡞Γ͸ूதͰ͸ͳ͘஝ੵ ○ ׬ᘳͳϓϩϯϓτΛ࡞Ζ͏ͱ͠ͳ͍ ○ ࢖͍ͳ͕ΒҭͯΔ ● มߋͷλΠϛϯά͸ࢦࣔͷத ○ ࢦࣔΛग़͢ ○ ˠҙʹ͙ͦΘͳ͍ ○ ˠमਖ਼Λ఻͑Δ ○ ˠͦͷྲྀΕͰNEϑΝΠϧͷमਖ਼΋ࢦࣔ ϓϩϯϓτ͸࡞ΒͤΔ΋ͷ

Slide 62

Slide 62 text

"*ʹΑΔύϥμΠϜγϑτ ਤ͕ίʔυʹ ͳΔʂ @Service public class DocumentApplicationService { private final DocumentRepository documentRepository; public DocumentApplicationService(DocumentRepository documentRepository) { this.documentRepository = documentRepository; }

Slide 63

Slide 63 text

 ΍ͬͯΈΑ͏ʂ

Slide 64

Slide 64 text

64

Slide 65

Slide 65 text

65 Profile nrs(成瀬 允宣) @nrslib プログラマ 趣味:カンファレンス講演 学生支援 小学校支援 写真