-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathJournal.txt
554 lines (353 loc) · 27.6 KB
/
Journal.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
January 23, 2025 - Thursday
===========================
[Refactoring: 3:30pm 2h30m]
* Changed method parameter from workspaceElementsFor(long id) -> workspaceElementsFor(Player), where the caller is doing: workspaceElementsFor(player.id().id())
1. Change signature to add Player parameter, using "Any var" -- this will pick up the player reference from the calling scope
2. Inline the id parameter (IntelliJ IDEA will complain, but it's wrong, so ignore it)
3. That's it
Could inline first, in which case the warning is correct, but then we can add Player as a parameter.
Prefer adding the parameter first so we don't get into a non-compilable situation.
January 22, 2025 - Wednesday
============================
* Maybe redo the hex tiles as pure HTML+CSS instead of images?
[Refactoring]
* 1:05pm (2h05m) - Refactored with Convert to Instance Method (to get it into the Fixture), then Move Method to Spy (and then remove unneeded Fixture parameter)
[Refactoring]
* (2:45pm | 3h45m) Did an alternative to the refactoring to extract a new ViewComponent:
1. Create new target class
2. Pass in the source of information to its constructor
3. Add temporary (immediately deprecated) getter for the info source
??4. Reformulate method access to use the getter
4a. Run tests!
5. Extract new method that takes the TARGET class as a parameter
6. Inline the old method to use the method created in step 5
7. Use CONVERT TO INSTANCE METHOD to move it to the new target class (via its parameter)
8. Inline the deprecated getter
January 21, 2025 - Tuesday
==========================
* Turns out you can send a <script> tag to the client over the WebSocket as long as it is targeting some ID (e.g., wrapped in a <swap> tag):
```
messageSender.sendMessage("<swap id='other-players'><script>document.location.href='/lobby';</script></swap>");
```
[Decision]
* Players have a Reference to Workspace. When collecting all Workspaces for Broadcasting a Game Update, I can either:
1. Ask Game for all Workspace (Game owns a List<Workspace>)
2. Ask Game for all Players, get Workspace from each Player, reduce to eliminate duplicates
==> I choose 2, and when Workspaces are shared among Players, I'll deal with eliminating the duplicates at that point
[Decision]
* Pawns are identified in the HTML as a DIV with the id of 'workspaceN-pawn' where N is 1-4
January 20, 2025 - Monday
=========================
* Nice Prepare refactoring at 3:50pm (4h50m)
* Was able to pass in Security Principal via .post() in the MockMvcTester!
* Deck discard pile now shows the proper "top" of pile card
* Decided not to make it a stack and just show the "last" card in the list (if there is any)
* We now properly handle RECONNECT, which required:
* Game has (currently) TWO states: CREATED and IN_PROGRESS
* Probably want CREATED to be WAITING_TO_START
* Game has a state() query method that returns the state enum
* MessageSendersForPlayers handles removal of MessageSender (which wraps WebSocketSession)
* Required a new method in our custom Multimap implementation
* Getting clunky to create tests at the application level, time for some Test Data Builders
* Having to always save Game after state changes is prone to error, so as much as possible for tests at the Application (Use Case) Layer, use OTHER Use Cases to do what's needed
January 16, 2025 - Thursday
===========================
[Decision]
* Keep Broadcaster#gameUpdate() at a high-level, with tests against it doing the minimal possible checking, and most of the transformation/rendering work is done by the individual ViewComponents
[Refactoring]
* Extract Class refactoring of DeckViewComponent (4pm)
* Created new target class
* Passed in DeckView to its constructor
* Added temporary getter for the DeckView
* Reformulated methods to use the DeckViewComponent.getter
* Inlined other methods
* Use MOVE METHOD refactoring recipe to move to DeckViewComponent
* Inlined the getter
January 15, 2025 - Wednesday
============================
* Learned about MockMvcTester and that it does not require Spring test annotations, so is much faster [h/t NotBjoggisAtAll]
* If the spring-boot-starter-validation is in the POM, then it'll get initialized, which takes over 400ms!
* IntelliJ IDEA Move Class refactoring doesn't offer to change visibility of methods/inner classes, unlike Move Method which does offer to "escalate" it
* Made the mistake (again!) of referencing a Game instance from a variable instead of retrieving it from the GameStore, so the Game I had was out of date
January 14, 2025 - Tuesday
==========================
* Card-related actions may require a pair of events, for example: to modify the Deck along with modifying the Player's Hand when discarding a card
!! Why isn't padding working on Dialog's CSS (but works for the child?) --> because Tailwind is overriding it (with a "CSS reset")
[Decision]
* "popup menu" when clicking on a Card in YOUR HAND does a GET request against an endpoint which returns a <dialog open ...> with buttons pre-filled with HX-POST htmlAttributes configured with the correct card name
* <dialog id="dialog">...</dialog> will always be in the page (starts off as empty) so we can replace it (via innerHTML) and not end up with lots of stray, unused dialog elements
* Versus tunneling the GET/POST through the WebSocket
* Use https://htmx.org/examples/keyboard-shortcuts/ for allowing a key to execute one of the buttons on the popup dialog
* We don't care about "which" specific instance of a CARD gets discarded/played, we will always show cards-in-hand in a pre-defined order
* Research: is there a fake back-end for easier testing of HTMX round-trips, e.g., do a GET, get some block of HTML returned, or do a POST and get some other block of HTML
January 13, 2025 - Monday
=========================
[Decision]
* Board layout: decided to use individual hex "tiles" laid out in a grid with overlapping rows (still need to figure out how to do that reliably in CSS)
* Has the advantage of easier layout adjustments, easier to determine where to put (draw) player pawns, etc.
[Design Decision]
* Relationship from Player to Tile is through the Workspace (see tldraw diagram for more details)
* Game knows Player -> Workspace is a 1..4:1 relationship: each player can have their own workspace, or can Pair/Ensemble to have multiple players for a single workspace, and therefore multiple players controlling a single pawn
* Game has Map<PlayerId, Workspace>: dispatches command from Player to their Workspace
* Actions flow from the Player+Hand to the Workspace, which then interacts with the Board, which then forwards the action on the Workspace's tile, that then does the necessary state change
* Workspace -> Pawn is a 1:1 relationship
* Workspace "owns" the Pawn (as well as Commit and Risk tracking)
* Board has Map of WorkspaceId->BoardTile
* Board has MultiMap of BoardTile->List<WorkspaceId> - this is so we can query a tile (for rendering) and ask for the workspaces (pawns) on it
* Nice little prepare refactoring in Player to support multiple events that are being played back (via the apply() method)
January 8, 2025 - Wednesday
===========================
* Refactoring a static method with parameter to instance method with field (~2:00 mark)
1. Create the private final field matching the method parameter
2. [auto] Generated constructor with field as parameter
3. Copy the static method to an instance method, dropping the parameter (so it uses the field)
4. In the static method, call the constructor with the parameter and then call the new instance method (from #3)
5. [auto] inline the static method into callers
January 7, 2025 - Tuesday
=========================
[Decision]
* Should "Component" things represent the "physical" way things are displayed on the screen (like GUI layouts), or is it more "logical"
* Advantage of representing the physical layout is easier to understand and map the objects to the screen
* Disadvantage is multiple objects representing the player's individual (personalized) view of things
I think the components should match the UI layout, which means at least two Components for custom rendering of a Player's view of their own hand+workspace, and the placeholder DIVs for the other-players view.
[Open Question]
* Should there be a ViewComponent tree that gets translated into an HtmlComponent tree, which can then be rendered into HTML text?
e.g., GameVC contains BoardVC, YouPlayerVC, OtherPlayersVC, etc., and OtherPlayersVC contains HandVC, which contains cards.
* "ForestHtmlComponent" is an HtmlComponent that does not render itself, only renders its children, which is useful for returning multiple HtmlComponents that are at the same level, i.e., multiple tree roots.
January 6, 2025 - Monday
========================
* Not sure what's going on with the EqualsVerifier in this test:
@Test
void swapEqualsTest() {
EqualsVerifier.forClass(HtmlComponent.Swap.class)
.withPrefabValues(HtmlComponent.class, HtmlComponent.swapDelete("red"), HtmlComponent.swapDelete("blue"))
.verify();
}
Documentation doesn't provide implementations of `Foo` and `Bar` in the explanation for the error we originally got: https://jqno.nl/equalsverifier/errormessages/recursive-datastructure/
* For multiple HtmlAttribute, where is the order of rendering defined (e.g., does "id=myId" come before or after "class=myClass")? Currently, it's only used in the DIV and the constructor defines the order of id first then class. Would be good to push this into a new "container" class (HtmlAttributes, plural) that has the List<HtmlAttribute>, a defined order, and knows how to render all of them in that order. [YAGNI]
January 1, 2025 - Wednesday
===========================
* Thought about this, but not doing it as it seems much easier just to build the expected tree structure and compare that directly
/*
assertThat(htmlElement)
.withTag(SWAP)
.withId("you")
.withAttribute("hx-swap-oob", "innerHTML")
.has(DIV)
.withClass("workspace")
.containsText("Workspace")
.has(DIV)
.withClass("titled-container")
.has(DIV)
.withClass("hand")
.containsText("less code")
*/
* Probably going with this style for the creation of the HTML tree structure:
HtmlComponent.Text nestedTextComponent = HtmlComponent
.div(HtmlComponent.text("text component contents"),
HtmlComponent.div(stuff))
.div(HtmlComponent.para("paragraph text"))
* Has discoverability (HtmlComponent [dot] auto-completion)
* Decided to create the empty (placeholder) DIVs for "Other Players" (filtering out "you") upon game start, so that later we broadcast to everyone the same content that targets those DIVs, without having to filter out the "you" player (since that DIV's target will be ignored)
December 31, 2024 - Tuesday
===========================
* Did lots of refactoring towards a nested component model for HTML generation, there were long periods where tests were failing, but mostly they were failing in the predicted way, so it wasn't a big deal
* If we need HTML escaping, we can put it in the TextComponent, once we ensure that we're not passing in HTML tags as content (e.g., <h2>Workspace</h2>)
December 30, 2024 - Monday
===========================
* CSS is hard for me - watch out for grid and nesting
* It's getting annoying to generate the HTML with nested blocks in a way that's testable, the indentation is fiddly
December 24, 2024 - Tuesday
===========================
* WebMvc slice tests don't do component scanning! Needed to manually pull in the MessageSenders into the TestConfig
* Good that we did a "spike" for figuring out how we will send HTML/X to the front-end, as it turns out we can't generate a custom game.html as we don't yet know how many players are in the game until the game starts
* Switching to shuffled/random deck didn't break any tests!
* A bit cumbersome writing a custom assertion for checking number of events in a list, need to see how it might be done better?
December 23, 2024 - Monday
==========================
* Be careful to configure Spring Beans properly as we don't currently have any tests to ensure that it is indeed configured with the correct components
* The AssertJ-JSoup library is meh, not quite what I need: while the HTML comparison failure is more concise (and resilient to whitespace differences), it's not a very "fluent" API
* Will need a library for generating the HTML and one (customized for AssertJ) for testing the HTML
* Having the WebSocketBroadcaster contain the Player to MessageSender map seems to be working nicely
* Need to refine the CSS in game.html so that it leverages the CSS nesting to do away with having a custom CSS name for each player
* Used the direct application of events to put cards in players hands: works for now, will that be problematic in the future (because we might not have the "correct" stream of events?)
* This should only be a temporary workaround for the fact that Player does not (yet) expose (publicly) the Player.drawCardFrom(ActionCardDeck) method
* The Broadcaster currently iterates through the Players in the Game to send out customized HTML, but really we want all MessageSenders _Connected_ to the Game to receive HTML: mostly for Players, but also for Observers/Watchers
December 17, 2024 - Tuesday
===========================
* Change in design: moved the "thing" that tracks the players and their associated WebSocket connections (sessions) from a shared adapter class, and instead can be part of the Broadcaster Outbound Adapter implementation. This is possible because the InBoundHandler wraps the WebSocketSession in a MessageSender, which is I/O-Free, and so can be handled inside the Application Layer. The InboundHandler now doesn't need to directly talk to the Player-Connection tracker, it adds/removes sessions (wrapped in MessageSenders) via the PlayerConnector use case (service).
* Discovered that we've been using GameHandle as a unique identifier for the Game aggregate, but we should use a more properly unique identifier for persistence.
November 20, 2024 - Wednesday
=============================
November 5, 2024 - Tuesday
==========================
* RULE: Don't use generified Event Records/Classes as Event objects as they're hard to serialize/deserialize in JSON and can't use Pattern Matching with generics
* Becoming more clear that the Broadcaster interface needs to be split, perhaps to a "pre-start" and a "post-start" set of interfaces
NEXT: verify that in the Broadcaster.gameUpdate(), it creates the htmx-HTML that has what we need to display the player cards, action deck, etc.
October 29, 2024 - Tuesday
==========================
* For next time: improve test output for events, more easily seeing which events are missing and/or out of order. May need to figure out what triggers IntelliJ IDEA's "compare" dialog for test output.
* Realized that we don't want to create the Deck entity when executing Game's start() method. We want to create the Deck when the events are played back during the apply().
* RULE: only create Entities and Value Objects when events are replayed via apply().
? Can we use Pattern Matching somehow to make this easier:
```
.filter(event -> event instanceof ActionCardDeckCreated)
.map(event -> (ActionCardDeckCreated) event)
```
* Re-learned that responding (via websocket) with HTML that contains an `hx-swap-oob` with `delete` will delete the HTML element from the page. This is what we want to remove the modal after the Start Game button is clicked.
* Used the following Thymeleaf syntax with the `hx-post` to get the button to do a POST to the correct endpoint:
```
th:hx-post="@{/game/{handle}/start-game(handle=${gameView.handle()})}"
hx-trigger="click"
```
October 24, 2024 - Thursday
===========================
* Turned out to be nicer to pass in the Game reference (really its enqueue method handle) to Deck's constructor rather than passing it to the draw() method. It makes the test author not have to understand how to properly create an "enqueuer" to make Deck work properly, but tests can still access the generated events via the events "receiver" (aka Accumulator).
* Deck's createForTest() methods getting a bit out of hand, so need to move to a more builder-like or lambda-based configuration mechanism
? Why did the th:href have /css as the base path for game.css: did that work before, or did something else change?
NEXT TIME: Have Game use the DeckFactory to create the Action Card Deck so when the game starts (via .start()), the deck is created properly. Then the PlayingGameMvcTest.post should pass.
October 23, 2024 - Wednesday
============================
* Set up _TODO_ filter to also find @Disabled tests (or some other way)
* Events should defensively copy any non-Value Objects like `List`
* Added messages to exceptions right away (instead of waiting until "later")
* Are we losing some sense of overall behavior by NOT having a test that is purely Commands + Queries (i.e., tests that don't mention Events at all) -- perhaps as a Use Case-oriented "acceptance test"
NEXT TIME: fix broken DeckTest:newActionCardDeck tests (missing enqueuer? or events?)
October 22, 2024 - Tuesday
==========================
* Decided that moving a card from the deck to the Player's hand is a two-event process (e.g., debit card from deck, credit card to player's hand)
* Makes applying events easier: we don't have to hand the deck to the Player's `apply()` method, only the event
* Preserves "conservation of cards" (or consistency of entities) at the Game's aggregate transaction level
+ Next time: implement the Deck Replenish event (command generates the Replenish, and applying Replenish puts the cards in the Draw pile)
+ Then we can go back and finish up the Deck CardDrawn event application (which eventually will cause us to replace drawPile.remove() with drawPile.peek())
October 17, 2024 - Thursday
===========================
* RULE: Never enqueue() (i.e., generate new) events during apply(), in fact, can we throw an exception if this happens??
* i.e., MUST NOT generate NEW events as the result of applying them
* RULE: Commands --Generate--> Events and Events --Change--> State (which might include creating entities/value objects), but we ENQUEUE events inside command methods, which does both STORE and APPLY those events
October 16, 2024 - Wednesday
============================
* Got tests passing for player drawing card from deck and finally did a commit
* (Re-)encountered need for isolating shuffling randomness for the deck when replenishing
* Discovered that Deck.view() was incomplete!
* Comments originally from Game.start() trying to figure out events and state change
// Deck starts with cards: [A..Z]
// stuff happens, deck now has [Y, Z]
// -> DeckReplenishedTo(Y, Z)
// player.drawToFull(deck)
// -> PlayerDrawFromDeck(gets Y)
// PlayerDrawFromDeck(gets Z)
// [ran out of cards, so shuffle to replenish] <-- what event here?
// PlayerDrawFromDeck(gets A)
// player has: Y, Z, A
// deck has B..Z
// Create new Deck by Shuffling the Discard Pile (Deck = [A..E])
// Player.drawToFull(deck)
// -> deck.draw(), deck.draw(), deck.draw() -> Player = [A, B, C], Deck = [D, E]
// COMMANDS -> EVENTS:
// (cmd) Create new Game("Game Name")
// (evt) GameCreated("Game Name", "handle")
// (cmd) Player 1 joins
// (evt) PlayerJoined("1")
// (cmd) Player 2 joins
// (evt) PlayerJoined("2")
// (state) Player1[], Player2[]
// (cmd) Start Game Command
// (evt) GameStarted()
// (evt) ActionCardDeckCreated(A,B,C,D,E)
// (state) Deck(draw)[](discard)[A,B,C,D,E]
// ** Player Draw to Full Hand Command
// ** Player 1 Draw Card from Deck
// -> DeckReplTo(A..E) // moves (shuffles) discard pile to draw pile
// -> PlayerDrewCard(1) -> A
// ** Player 1 Draw Card from Deck
// -> PlayerDrewCard(1) -> B
// ** Player 1 Draw Card from Deck
// -> PlayerDrewCard(1) -> C
// ** Player 1 Discards "B"
// -> PlayerDiscards(1, B)
// ** Player 2 Draw Card from Deck
// -> PlayerDrewCard(2) -> D
// ** Player 2 Draw Card from Deck
// -> PlayerDrewCard(2) -> E
// ** Player 2 Draw Card from Deck
// -> DeckReplFromDiscard(B) -> Deck[B]
// -> PlayerDrewCard(2) -> B
// REBUILD:
// --events-- --resulting state--
// DeckReplTo(A..E) -> Player1 [], Deck = [A..E]
// PlayerDrewCard(1) -> Player1 [A], Deck [B..E][]
// PlayerDrewCard(1) -> Player1 [A, B], Deck [C..E][]
// PlayerDrewCard(1) -> Player1 [A, B, C], Deck [D..E][]
// PlayerDiscards(1, B) -> Player1 [A, C], Deck [D..E][B]
// PlayerDrewCard(2) -> Player2 [D], Deck [E][]
// PlayerDrewCard(2) -> Player2 [D, E], Deck [][]
// DeckRepl(B) -> Player1 [A, C], Player 2 [D, E], Deck [B][]
// PlayerDrewCard(2) -> Player1 [A, C], Player 2 [D, E, B], Deck [][]
// next player...
// Player2.drawToFull(deck)
// -> deck.draw() [D], deck.draw() [E], DeckReplenishTo[X, Y, Z] (via command, right before enqueue), deck.draw() [X]
// Player2.uncommitedEvents() => PD(D), PD(E), REPL(X,Y,Z), PD(X)
// (Player2 has D, E, X) (Deck has Y, Z)
// PlayerDrew(D)
// PlayerDrew(E)
// DeckReplenishedTo(X, Y, Z)
// PlayerDrew(X)
// REBUILD:
// DeckReplTo(A..E) -> Deck = [A..E]
// PlayerDrew(1, A) -> Player1 [A], Deck [B..E]
// PlayerDrew(1, B) -> Player1 [A, B], Deck [C..E]
// PlayerDrew(1, C) -> Player1 [A, B, C], Deck [D..E]
// PlayerDrew(2, D) -> Player2 [D], Deck [E]
// PlayerDrew(2, E) -> Player2 [E], Deck []
// DeckReplenishedTo(X, Y, Z) -> Deck [X, Y, Z]
// PlayerDrew(2, X) -> Player2 [X], Deck [Y, Z]
// RECONSTITUTE FROM PERSISTENCE
// DeckReplenishedTo(Y, Z)
// PlayerDrewCard(Y), [applying this does not modify Deck]
// PlayerDrewCard(Z),
// PlayerDrewCard(A),
// DeckReplenishedTo(B..Z)
// player has: Y, Z, A
// deck has: B..Z
// EVENTS: PlayerDrewCard() [applying this asks player to draw card from deck]
// PlayerDrewCard(),
// -- deck ran out of cards in Draw Pile: shuffle happens --
// DeckReplenishedTo(A..Z)
// PlayerDrewCard()
October 15, 2024 - Tuesday
==========================
* Recovered most of our context
* Created sequence diagram to document what happens when the "start game" scenario is started, clarifying where the "fresh" events go (a new container each transaction)
* Decided to have Game's apply loop dispatch events specific for Player and Deck directly to each of those entities (instead of sending the events to everyone)
NEXT: get the currently failing test to pass, implement player drawing cards to full hand inside of Game.start(), then refactor to move that behavior into Player
June 13, 2024 - Thursday
========================
* Event-sourcing with entities (like Deck and Player) that are part of the Aggregate, is quite unfamiliar
* It's even worse when combining ES with also be not 100% comfortable with "Nullable Infra Wrappers"
* Spelling out precisely the events that would get generated and then how the reconstitution (rebuild? replay?) happens, both when the player just draws cards from a Deck that has enough, as well as when the Deck has to be replenished via shuffling
* Switch from "reconstitute" to "rebuild" (easier to say and type)
June 5, 2024 - Wednesday
========================
* Shifting my mindset to a more event-sourced approach for storing state instead of mapping an entire object graph to a database. With the events, I don't worry about how to store objects like the Player directly in the database.
* Still struggling with committing more often
* Cody does a good job of summarizing what I've done based on commit messages. I wish it could also look at the code itself and summarize what I've done based on the code.
* Decided that the PlayerId is not really needed, as the Player can be identified by the MemberId in the context of a specific Game. It does mean that I can't have a Member act as multiple Players in a single Game.
* Copy-pasting the code from Blackjack was helpful to move quickly with the event-sourcing, but forgot to copy the code that was handling "fresh events" separately from the events used to reconstitute the Game.
* Keep making the mistake of storing whole object references in the in-memory store (repository), which leads to code that works when I'm able to reference the same object, instead of the realistic case of restoring objects from persistence, and then throwing them away after saving.
* Need to submit a feature request for IntelliJ IDEA to find usages based on a Record's constructor
* Here's a copy for reference of the summary that Cody created based on commit messages:
* Refactoring and Preparation for Player Hand: You started by refactoring the PlayerDrewActionCard event to use MemberId instead of PlayerId, as the former is what is received from the outside world. This change also introduced a limitation where a single Member cannot act as multiple Players in a single Game. Additionally, you prepared for the Player to have a "hand" consisting of ActionCard objects.
* Game Start and Player Drawing Cards: You implemented the game start functionality, where the game.start() method enqueues the GameStarted event. Furthermore, you added a test to demonstrate that multiple cards of the same type can be added to a player's hand. At this stage, the game start process involves only the first player drawing a single (hard-coded) card, which generates an event for that player.
* Player Hand Management: You introduced the concept of a Player's hand, where the Player can keep track of cards added to their hand. This involved creating a new event, PlayerDrewActionCard, but it did not yet change the state of the game.
* Event-Sourcing and Persistence: You converted the GameStore from storing Game object references to storing Events (as DTOs) and reconstituting the Game objects from those DTOs. This change more closely emulates real persistence and brings the project closer to that goal. Additionally, you added an EventDto (somewhat generalized) with a test, copied from the Blackjack project.
* Join Sequence and Game Start Details: Finally, you updated the join sequence to include more details around starting a game.
May 29, 2024 - Wednesday
========================
* Struggled with CSS to create the layout for the "Your Hand" section of the Game
* Added shuffling of the discard pile to the draw pile in the Deck, using embedded stub a la Nullables
* Lot of thinking about design around UI Components
* Decided (for now) to use POST for sending commands instead of going through the WebSocket
* Added endpoint for the POST to start game
* Decided to rely on the Game's events (via freshEvents) to check for behavior/state change - this currently causes a test to fail, since we didn't clear freshEvents
* Next time need to properly handle GameStore's find so that we can rely on freshEvents to be cleared when loaded