New milestone MIL-008 with story US-001.06, and the design documents updated for the level choice: BC-001, PP-001, US-001, UC-001, DM-001, DICT-001, SSD-001, OC-001, SD-001, DCD-001 and the wireframe. Review records RC-030 to RC-040, all Go (autonomous mode, not independent). Task: MIL-008#1
213 lines
6.0 KiB
Markdown
213 lines
6.0 KiB
Markdown
# Sequence Diagrams: Study Flash Cards
|
|
|
|
## Metadata
|
|
| Key | Value |
|
|
| --- | --- |
|
|
| ID | SD-001 |
|
|
| CrossReference | [OC-001], [DCD-001], [DICT-001] |
|
|
|
|
## Version History
|
|
| Date | Status | Author | Reviewer | Change | Commit |
|
|
| --- | --- | --- | --- | --- | --- |
|
|
| 2026-10-14 | Deprecated | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | [c3fbcbc] |
|
|
| 2026-10-14 | Accepted | Jens Tirsvad Nielsen | AI as stand-in for S01 | Added the sequences startStudySession (levels) and chooseLevel (level filter) | pending |
|
|
|
|
---
|
|
|
|
## Sequence: startStudySession
|
|
|
|
**Realizes:** `startStudySession` in [OC-001]
|
|
|
|
### Diagram
|
|
|
|
```plantuml
|
|
@startuml
|
|
participant ":LevelView" as V
|
|
participant ":LevelViewModel" as VM
|
|
V -> VM : choices
|
|
VM --> V : tuple[Level, ...]
|
|
@enduml
|
|
```
|
|
|
|
### Pattern Annotations
|
|
|
|
| Pattern (GRASP / GoF) | Applied to | Rationale |
|
|
| --- | --- | --- |
|
|
| MVVM | `LevelView`, `LevelViewModel` | The view shows the choices; the labels and the order are in the view model |
|
|
|
|
### Postcondition Coverage
|
|
|
|
| Postcondition (from contract) | Satisfied by message |
|
|
| --- | --- |
|
|
| The three levels are returned in order | `choices` |
|
|
| No session or deck is created and no file is read | no other message is sent |
|
|
|
|
### Responsibility Check
|
|
|
|
The view model owns the choices; the view only draws buttons and forwards the click.
|
|
|
|
## Sequence: chooseLevel
|
|
|
|
**Realizes:** `chooseLevel` in [OC-001]
|
|
|
|
### Diagram
|
|
|
|
```plantuml
|
|
@startuml
|
|
participant "main : app" as M
|
|
participant ":CsvVocabularyRepository" as VR
|
|
participant ":CsvProgressRepository" as PR
|
|
participant "level : Level" as L
|
|
participant ":StudySession" as S
|
|
participant ":Deck" as D
|
|
participant ":SpeechPort" as SP
|
|
M -> VR : load()
|
|
VR --> M : vocabulary
|
|
M -> PR : load()
|
|
PR --> M : deck | None
|
|
alt deck is None
|
|
create D
|
|
M -> D : from_vocabulary(vocabulary)
|
|
D --> M : deck
|
|
end
|
|
M -> L : first(vocabulary.cards)
|
|
L --> M : level_cards
|
|
create S
|
|
M -> S : create(deck, progress, speech, choose, level_cards)
|
|
M -> S : next_card()
|
|
activate S
|
|
S -> D : within(level_cards)
|
|
D --> S : candidates
|
|
opt candidates is not empty
|
|
S -> D : draw(choose, exclude, among=candidates)
|
|
D --> S : card
|
|
S -> SP : speak(card.front, front_code)
|
|
end
|
|
S --> M : CardDto | None
|
|
deactivate S
|
|
@enduml
|
|
```
|
|
|
|
### Pattern Annotations
|
|
|
|
| Pattern (GRASP / GoF) | Applied to | Rationale |
|
|
| --- | --- | --- |
|
|
| Creator | `app.main` creates `StudySession` | It holds the adapters the session needs |
|
|
| Information Expert | `Level.first`, `Deck.within`, `Deck.draw` | The level knows its limit, the deck knows its cards |
|
|
| Null Object | `NullSpeech` as `SpeechPort` | The session never tests for speech |
|
|
|
|
### Postcondition Coverage
|
|
|
|
| Postcondition (from contract) | Satisfied by message |
|
|
| --- | --- |
|
|
| A `StudySession` was created and associated with the level | `create(deck, progress, speech, choose, level_cards)` |
|
|
| A `Deck` was created from the progress file or a copy of the `Vocabulary`, cards outside the level kept | `PR.load()`, `alt` fragment, `from_vocabulary` |
|
|
| The level's cards are the first N cards of the `Vocabulary` | `first(vocabulary.cards)` |
|
|
| `current_card` set and a `CardDto` returned, or `None`; the word was passed to the `SpeechPort` | `next_card()` with `within`, the `opt` fragment and `speak` |
|
|
|
|
### Responsibility Check
|
|
|
|
The composition root only wires; the level selects, the deck holds, the session coordinates; no object receives all messages.
|
|
|
|
## Sequence: markKnown
|
|
|
|
**Realizes:** `markKnown` in [OC-001]
|
|
|
|
### Diagram
|
|
|
|
```plantuml
|
|
@startuml
|
|
participant ":FlashCardView" as V
|
|
participant ":FlashCardViewModel" as VM
|
|
participant ":StudySession" as S
|
|
participant ":Deck" as D
|
|
participant ":CsvProgressRepository" as PR
|
|
V -> VM : is_known()
|
|
activate VM
|
|
VM -> S : mark_known()
|
|
activate S
|
|
opt current_card is not None
|
|
S -> D : remove(card)
|
|
S -> PR : save(deck)
|
|
end
|
|
deactivate S
|
|
VM -> S : next_card()
|
|
S --> VM : CardDto | None
|
|
VM --> V : side, language name, text
|
|
deactivate VM
|
|
@enduml
|
|
```
|
|
|
|
### Pattern Annotations
|
|
|
|
| Pattern (GRASP / GoF) | Applied to | Rationale |
|
|
| --- | --- | --- |
|
|
| MVVM | `FlashCardView`, `FlashCardViewModel` | The view raises the event and shows the result; the rule is behind the view model |
|
|
| Repository | `CsvProgressRepository` | The session stores the deck through a port |
|
|
|
|
### Postcondition Coverage
|
|
|
|
| Postcondition (from contract) | Satisfied by message |
|
|
| --- | --- |
|
|
| The card was removed from the `Deck` | `remove(card)` |
|
|
| The `Deck` was stored | `save(deck)` |
|
|
| `current_card` was set to `None` | `mark_known()` ends with no current card; `next_card()` then draws the next |
|
|
|
|
### Responsibility Check
|
|
|
|
The view model only sequences two session calls and maps the result for the view.
|
|
|
|
## Sequence: markUnknown
|
|
|
|
**Realizes:** `markUnknown` in [OC-001]
|
|
|
|
### Diagram
|
|
|
|
```plantuml
|
|
@startuml
|
|
participant ":FlashCardView" as V
|
|
participant ":FlashCardViewModel" as VM
|
|
participant ":StudySession" as S
|
|
participant ":Deck" as D
|
|
participant ":SpeechPort" as SP
|
|
V -> VM : next_card()
|
|
VM -> S : next_card()
|
|
activate S
|
|
S -> D : within(level_cards)
|
|
D --> S : candidates
|
|
opt candidates is not empty
|
|
S -> D : draw(choose, exclude=current, among=candidates)
|
|
D --> S : card
|
|
S -> SP : speak(card.front, front_code)
|
|
end
|
|
S --> VM : CardDto | None
|
|
deactivate S
|
|
VM --> V : side, language name, text
|
|
@enduml
|
|
```
|
|
|
|
### Pattern Annotations
|
|
|
|
| Pattern (GRASP / GoF) | Applied to | Rationale |
|
|
| --- | --- | --- |
|
|
| Information Expert | `Deck.draw` | The deck knows its cards |
|
|
|
|
### Postcondition Coverage
|
|
|
|
| Postcondition (from contract) | Satisfied by message |
|
|
| --- | --- |
|
|
| A different card of the level was chosen when two or more exist | `within(level_cards)`, `draw(choose, exclude=current, among=candidates)` |
|
|
| Deck and file unchanged | no `remove` or `save` message |
|
|
| The Malay word was passed to the `SpeechPort` | `speak(card.front, front_code)` |
|
|
|
|
### Responsibility Check
|
|
|
|
Same as `markKnown`; the session is the only object that speaks and draws.
|
|
|
|
---
|
|
|
|
[OC-001]: ./oc.md
|
|
[DCD-001]: ../dcd.md
|
|
[DICT-001]: ../dictionary.md
|
|
[c3fbcbc]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/031-flash-card/commit/c3fbcbcfaf799e0ae34aed7e14d3bc8a62f03db3
|