Introduction to Software Engineering · Chapter 06

Lab-06: Unified Modeling Language (UML)

Your team turns the Lab-05 requirements into the core UML models of ITC Club Hub: a use case diagram, a domain class diagram that you translate into C++ headers, a sequence diagram for "register for an event", an activity diagram for check-in at the door, and a state machine for Registration that you implement and test in C++. The challenge models the monthly activity report and gives a component overview of the whole system.

UML 2.5.1 PlantUML · Mermaid · C++20 · CMake · GoogleTest · GitHub ≈ 5 hours of team work 5 tasks + 1 challenge
How to work through this lab. Do the tasks in order; each builds on the previous. Read the Goal, follow the Steps, compare with the Expected output, and tick the Acceptance checklist (saved in your browser). Open a hint only when stuck for more than 10 minutes. This is team work: split the tasks between members, but everyone reviews every diagram, and every member's work must be visible as their own commits in the Git history.
0

Setup

20 min
  1. Tools. Diagram sources are plain text: PlantUML (.puml) for the use case diagram and Mermaid (.mmd) for the others. Install VS Code with the extensions PlantUML (jebbs) and Markdown Preview Mermaid Support, or use the online editors plantuml.com/plantuml and mermaid.live to preview and export PNG. For the C++ part you need the toolchain from earlier labs: a C++20 compiler (GCC 13, Clang 17 or Visual Studio 2022), CMake 3.28 or later, Git.
    git switch main && git pull
    git switch -c lab-06
    cmake --version        # 3.28 or later
    g++ --version          # or clang++ / cl: must support C++20
    java -version          # optional: only to render PlantUML locally
    # optional command-line export of Mermaid diagrams (needs Node.js)
    npx -p @mermaid-js/mermaid-cli mmdc -h
  2. Inputs from earlier labs. This lab turns the Lab-05 requirements into models and extends the C++ project from earlier labs. If one of your earlier deliverables is incomplete, use the fallback; do not stop to repair the earlier lab.
    InputFile (your team's name may differ)What you need from itFallback if missing
    User storieslab05/user-stories.mdRoles ("As a ...") become actors; goals ("I want to ...") become use cases (Task 1)The feature list in the case-study brief below; one story per bullet.
    Textual use caselab05/use-case-register-for-event.mdMain flow and alternative flows of "Register for event" (Task 4)Main flow: student picks an event, system checks it is in the future, not full and not already registered, stores the registration, confirms. Alternatives: event full, already registered, event in the past.
    SRSlab05/srs.mdGlossary, data items and business rules BR-01..BR-05 (Tasks 2 and 5)Business rules and personal data in the case-study brief.
    Traceability matrixlab05/traceability-matrix.csvStory ids to link use cases back to stories (Task 1)Number the stories US-01.. yourself in lab06/use-case-notes.md.
    C++ projectCMakeLists.txt, include/clubhub/, src/, tests/A building project with GoogleTest via FetchContent (Tasks 3 and 5)The minimal CMakeLists.txt in the Task 3 starter.
  3. Case-study brief. The box below holds every fact about ITC Club Hub that this lab needs.
    Case-study brief: ITC Club Hub
    Purpose
    An application for student clubs at the Institute of Technology of Cambodia. Teams model it as a web and mobile system and implement its core in C++20: domain model, business rules, a command-line front end and CSV or JSON file storage.
    Actors
    Student browses events, registers, cancels and receives a reminder. Organiser checks registered students in at the door and sees attendance. Club Leader registers a club and publishes its events. Administrator approves clubs and sees a monthly activity report across clubs. Email Service is an external system that delivers reminders.
    Data
    Club: name, description, status (Pending, Approved, Rejected). Event: title, date and time, location, capacity, description. Student: student ID (for example e20230001), name, email, phone (optional), club membership. Registration: status (Registered, Cancelled, CheckedIn, Waitlisted), time of registration; attendance is derived from check-ins.
    Business rules
    BR-01 capacity cannot be exceeded (a waiting list is a Should feature) · BR-02 a student cannot register twice for the same event · BR-03 cancellation closes 24 hours before the start · BR-04 events in the past cannot be registered for · BR-05 only approved clubs can publish events. If your Lab-05 SRS numbers them differently, use your numbers.
    Code names
    Namespace clubhub; classes Club, Event, Student, Registration; enums ClubStatus, RegistrationStatus; RegistrationService with registerStudent, cancel, checkIn; interfaces EventRepository, RegistrationRepository with CSV-backed implementations. Layout: CMakeLists.txt, src/, include/clubhub/, tests/, .github/workflows/ci.yml.
    Rhythm
    Week 7: this lab. Week 8: midterm and project checkpoint 1, where these models are reviewed. Sprint 1 (weeks 9 and 10) implements the first stories from them. Roles: one Product Owner, a rotating Scrum Master, the rest Developers.
  4. Deliverable folder. Create lab06/ with these files; every task fills some of them.
    clubhub/
    ├── lab05/                              # earlier lab, read-only here
    ├── lab06/
    │   ├── use-case-diagram.puml + .png    # Task 1
    │   ├── use-case-notes.md               # Task 1
    │   ├── class-diagram.mmd + .png        # Task 2
    │   ├── consistency.md                  # Task 3
    │   ├── sequence-register.mmd + .png    # Task 4
    │   ├── activity-checkin.mmd + .png     # Task 4
    │   ├── state-registration.mmd + .png   # Task 5
    │   ├── state-table.md                  # Task 5
    │   ├── README.md                       # Task 5 (record what changed)
    │   └── challenge/                      # Challenge (optional)
    ├── include/clubhub/                    # Tasks 3 and 5: *.hpp
    ├── src/                                # Tasks 3 and 5: *.cpp
    └── tests/registration_rules_test.cpp   # Task 5
  5. Conventions. Every diagram source starts with a title, a version and a date (' v1.0 2026-10-.. in PlantUML, %% v1.0 2026-10-.. in Mermaid). Names in diagrams are the code names from the brief, spelled identically. Commit the source and the exported PNG together, one commit per diagram at least.
1

Use case diagram from the user stories

Easy 40 min · led by the Product Owner

Goal

Show who uses ITC Club Hub and for which goals in one PlantUML use case diagram, derived from your Lab-05 user stories, with one justified «include» and one justified «extend».

Steps

  1. Go through lab05/user-stories.md. Every role in "As a <role>" is an actor candidate; merge synonyms ("member" and "student" are one role). You should end with Student, Organiser, Club Leader, Administrator and the secondary actor Email Service.
  2. Turn every goal ("I want to ...") into a use case named verb + object ("Register for event", "Approve club"). Drop user-interface steps such as "click", "type" or "open the menu". Aim for 10 to 14 use cases.
  3. Write lab06/use-case-diagram.puml from the starter: left to right direction, a rectangle "ITC Club Hub" as system boundary with all use cases inside, actors outside, one association line per actor goal.
  4. Add exactly one «include» (a step that two use cases always share, for example "Identify student") and one «extend» (optional behaviour under a condition, for example "Join waiting list" when the event is full). Check the arrow directions: include from base to included, extend from extension to base.
  5. In lab06/use-case-notes.md, justify each of the two relationships in two or three sentences, and add a table use case → story ids. Every Must story must appear in at least one row.
  6. Export lab06/use-case-diagram.png (VS Code: PlantUML: Export Current Diagram; or java -jar plantuml.jar -tpng lab06/use-case-diagram.puml) and commit the source, the PNG and the notes.

Starter

@startuml
' ITC Club Hub: use case diagram · v1.0 · 2026-10-..
left to right direction
actor Student
actor Organiser
actor "Club Leader" as Leader
actor Administrator as Admin
actor "Email Service" as Mail

rectangle "ITC Club Hub" {
  usecase "Browse events" as UC1
  usecase "Register for event" as UC2
  ' TODO: one usecase line per goal from lab05/user-stories.md
}

Student -- UC1
Student -- UC2
' TODO: associations for Organiser, Leader, Admin, Mail

' TODO: one include and one extend, for example
' UC2 ..> UCx : <<include>>
' UCy ..> UC2 : <<extend>>
@enduml

Expected output

A diagram shaped like the one on slide 8 of the chapter, but built from your stories. An excerpt of a good use-case-notes.md:

## Relationships
- «include» Register for event → Identify student: both "Register for
  event" (US-04) and "Check in student" (US-11) always identify the
  student by student ID first; the shared steps are written once.
- «extend» Join waiting list → Register for event: only when the event
  is full (BR-01) does the student get the waiting-list offer (US-06,
  Should).

## Use case to story
| Use case            | Actor(s)                | Stories      |
|---------------------|-------------------------|--------------|
| Register for event  | Student                 | US-04, US-05 |
| Check in student    | Organiser               | US-11        |
| Send reminder       | Email Service           | US-08        |

Show hints

  • An actor is a role, never a person ("Dara") or a job title outside the system ("Dean").
  • A reminder that is sent automatically is still a use case; its primary trigger is time, and the Email Service is the secondary actor that delivers it.
  • If PlantUML shows actors inside the rectangle, you declared them inside the rectangle { } block: move the actor lines above it.
  • «include» and «extend» are optional in general, but this task requires one of each so that you practise the arrow directions.

Acceptance checklist

2

Domain class diagram in Mermaid

Easy 45 min · two Developers, reviewed by the Product Owner

Goal

Model the Club Hub domain as a Mermaid classDiagram with typed attributes, associations with multiplicities on both ends, one composition and the two enumerations.

Steps

  1. Underline the nouns in the SRS and the stories. Keep as classes the concepts with their own identity and data: Club, Event, Student, Registration. Nouns that are just a value ("location", "capacity") become attributes.
  2. In lab06/class-diagram.mmd, give each class all attributes from the brief with visibility and type: -id: int, -start: sys_seconds, -phone: optional~string~ (Mermaid writes generics with ~).
  3. Add ClubStatus (Pending, Approved, Rejected) and RegistrationStatus (Registered, Cancelled, CheckedIn, Waitlisted) with the <<enumeration>> annotation, and use them as attribute types.
  4. Draw the associations with a multiplicity on both ends: exactly one composition Club "1" *-- "0..*" Event, plus Event "1" -- "0..*" Registration and Student "1" -- "0..*" Registration. Add club membership (Student "0..*" -- "0..*" Club) if your stories need it.
  5. Add the operations the business rules need: Club +approve(), +publish(e: Event); Registration +cancel(now, eventStart), +checkIn(); Event +isInPast(now) bool.
  6. Test the model with one real situation: can you place "Dara is registered for the Arduino Workshop of the Robotics Club, and is on the waiting list of the Robot Race"? If not, fix the diagram. Export lab06/class-diagram.png (mermaid.live, or npx -p @mermaid-js/mermaid-cli mmdc -i lab06/class-diagram.mmd -o lab06/class-diagram.png) and commit.

Starter

%% ITC Club Hub: domain class diagram · v1.0 · 2026-10-..
classDiagram
  class Club {
    -id: int
    -name: string
    -status: ClubStatus
    +approve() void
    +publish(e: Event) void
  }
  class Event {
    -id: int
    -title: string
    %% TODO: start, location, capacity, description, isInPast(now)
  }
  class Student {
    %% TODO: studentId, name, email, phone (0..1)
  }
  class Registration {
    %% TODO: id, status, registeredAt, cancel(now, eventStart), checkIn()
  }
  class ClubStatus {
    <<enumeration>>
    Pending
    Approved
    Rejected
  }
  %% TODO: RegistrationStatus
  Club "1" *-- "0..*" Event : publishes
  %% TODO: the two associations of Registration

Expected output

The Registration corner of a good diagram (your complete diagram has all four classes and both enumerations):

classDiagram
  class Event {
    -id: int
    -capacity: int
    +isInPast(now) bool
  }
  class Student {
    -studentId: string
    -phone: optional~string~
  }
  class Registration {
    -id: int
    -status: RegistrationStatus
    -registeredAt: sys_seconds
    +cancel(now, eventStart) void
    +checkIn() void
  }
  class RegistrationStatus {
    <<enumeration>>
    Registered
    Cancelled
    CheckedIn
    Waitlisted
  }
  Event "1" -- "0..*" Registration
  Student "1" -- "0..*" Registration
  Registration ..> RegistrationStatus

Show hints

  • Do not put event: Event as an attribute of Registration and draw the association: show the link once, as the line.
  • Composition fits Club/Event because an event cannot exist without its club and is deleted with it. A registration is not "owned" by a student in that sense: use a plain association.
  • Inside a <pre> or Markdown file you can write <<enumeration>> directly; only HTML pages need it escaped.
  • If mermaid.live reports a parse error, check for a missing closing brace or a space between a class name and { on the same line.

Acceptance checklist

3

From the class diagram to C++ headers

Medium 60 min · each Developer writes one header

Goal

Translate the class diagram into four headers in include/clubhub/ that compile with the team's CMake project, and prove with a consistency table that diagram and code contain exactly the same classes, attributes, associations and enum values.

Steps

  1. Create include/clubhub/event.hpp, club.hpp, student.hpp and registration.hpp: #pragma once, namespace clubhub, one class per header. Split the four headers between the Developers so that each appears in a different member's commit.
  2. Apply the mapping from the chapter: - attributes become private members with a trailing underscore and const getters; + operations become public member functions; enumerations become enum class (ClubStatus in club.hpp, RegistrationStatus in registration.hpp); time is std::chrono::sys_seconds.
  3. Map the relationships: the composition becomes std::vector<Event> events_; in Club; the associations of Registration become ids (int eventId_;, std::string studentId_;); 0..1 becomes std::optional<std::string> phone_;. No raw new or owning pointers.
  4. Put member function bodies that are more than one line in src/club.cpp and src/registration.cpp (for example Club::publish throws std::logic_error unless the club is Approved, rule BR-05) and add both files to the clubhub_core library in CMakeLists.txt.
  5. Build from a clean folder: cmake -S . -B build && cmake --build build. Fix every warning.
  6. Fill lab06/consistency.md: one row per class, attribute, association end and enum value, with its place in code. Then read the headers and add a row for anything in code that is missing from the diagram. Fix the diagram or the code until every row is ✓.

Starter

// include/clubhub/registration.hpp
#pragma once
#include <chrono>
#include <stdexcept>
#include <string>

namespace clubhub {

enum class RegistrationStatus { Registered, Cancelled, CheckedIn, Waitlisted };

// thrown when a business rule (BR-01..BR-05) rejects an operation
class RegistrationError : public std::runtime_error {
public:
    using std::runtime_error::runtime_error;
};

class Registration {
public:
    Registration(int id, int eventId, std::string studentId,
                 std::chrono::sys_seconds registeredAt);

    void cancel(std::chrono::sys_seconds now,
                std::chrono::sys_seconds eventStart);
    void checkIn();

    int id() const { return id_; }
    int eventId() const { return eventId_; }
    // TODO: studentId(), status(), registeredAt()

private:
    int id_;
    int eventId_;                 // association Registration -- "1" Event
    // TODO: studentId_ (association to Student), status_, registeredAt_
};

} // namespace clubhub
# CMakeLists.txt (fallback if your project has no core library yet)
cmake_minimum_required(VERSION 3.28)
project(clubhub LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_library(clubhub_core
    src/club.cpp
    src/registration.cpp
)
target_include_directories(clubhub_core PUBLIC include)
if(MSVC)
    target_compile_options(clubhub_core PRIVATE /W4)
else()
    target_compile_options(clubhub_core PRIVATE -Wall -Wextra -Wpedantic)
endif()

Expected output

$ cmake -S . -B build && cmake --build build
-- Configuring done
-- Generating done
[ 33%] Building CXX object CMakeFiles/clubhub_core.dir/src/club.cpp.o
[ 66%] Building CXX object CMakeFiles/clubhub_core.dir/src/registration.cpp.o
[100%] Linking CXX static library libclubhub_core.a
[100%] Built target clubhub_core
| Diagram element                        | C++                                         | ✓ |
|----------------------------------------|---------------------------------------------|---|
| class Registration                     | include/clubhub/registration.hpp            | ✓ |
| Registration -status: RegistrationStatus | Registration::status_                     | ✓ |
| Event "1" -- "0..*" Registration       | Registration::eventId_ (many side: repo)    | ✓ |
| Club "1" *-- "0..*" Event              | Club::events_ (std::vector<Event>)          | ✓ |
| Student -phone [0..1]                  | Student::phone_ (std::optional)             | ✓ |
| RegistrationStatus::Waitlisted         | enum class RegistrationStatus               | ✓ |
| (code only) Event::description_        | added to the diagram in v1.1                | ✓ |

Show hints

  • A header that is never included is never compiled. src/club.cpp includes club.hpp, which includes event.hpp; make sure student.hpp and registration.hpp are included by some .cpp too.
  • Why ids instead of const Event&? Registrations and events are loaded from different CSV files; a reference would dangle after a reload. The chapter's slide 12 explains it.
  • std::chrono::sys_seconds needs <chrono>; build a value with std::chrono::sys_days{2026y / std::chrono::October / 15} + 14h.
  • The "many" side of an association (an event's registrations) is usually not a member: a repository answers that question later.

Acceptance checklist

4

Sequence diagram and activity diagram

Medium 60 min · two pairs in parallel, Scrum Master runs the walk-through

Goal

Model the behaviour of two scenarios: "register for an event" as a sequence diagram whose messages are real C++ function names, including the full-event and already-registered alternatives, and "check-in at the door" as an activity diagram with decisions, a fork/join and one lane per participant.

Steps

  1. Pair A reads the textual use case from Lab-05 and lists the main flow and the alternative flows event is full (BR-01) and already registered (BR-02). Pair B lists the check-in steps as an organiser at the door would do them.
  2. Pair A writes lab06/sequence-register.mmd with the lifelines Student (actor), CLI, RegistrationService, EventRepository, RegistrationRepository, and messages registerStudent(studentId, eventId, now), findById, findActive(studentId, eventId), countActive(eventId), save(registration) with dashed returns.
  3. Pair A adds one alt fragment with three operands: [already registered], [event is full], [else]; each ends with the message the CLI prints. Show the activation of RegistrationService.
  4. Pair B writes lab06/activity-checkin.mmd as a flowchart LR: start and end nodes (@{ shape: sm-circ }, @{ shape: fr-circ }), one subgraph per lane (Student, Organiser, Club Hub), decisions registered for this event? and already checked in? with a guard on every outgoing branch, and a fork/join (@{ shape: fork }, @{ shape: join }) around "mark CheckedIn" and "update attendance count".
  5. Walk-through (Scrum Master): one member plays each lifeline and reads the messages aloud. Every message must exist as a declaration in include/clubhub/; add the missing ones to registration_service.hpp, event_repository.hpp or registration_repository.hpp (declarations only, bodies come in Sprint 1).
  6. Export both PNGs, commit sources, PNGs and the new declarations, and rebuild to be sure the headers still compile.

Starter

%% Register for an event: main flow and alternatives · v1.0 · 2026-10-..
sequenceDiagram
  actor S as Student
  participant CLI
  participant RS as RegistrationService
  participant ER as EventRepository
  participant RR as RegistrationRepository
  S->>CLI: register 12
  CLI->>RS: registerStudent("e20230001", 12, now)
  activate RS
  RS->>ER: findById(12)
  ER-->>RS: Event
  %% TODO: findActive and countActive calls
  alt already registered
    RS-->>CLI: throw RegistrationError
    CLI-->>S: "You are already registered"
  else event is full
    %% TODO
  else
    %% TODO: save(registration), return, confirmation
  end
  deactivate RS

Expected output

The shape of a good activity diagram (your version adds the already checked in? decision and your own action names):

flowchart LR
  start@{ shape: sm-circ, label: "start" }
  subgraph O["Organiser"]
    o1[Type student ID]
    d1{Registered?}
    o3[Send to club desk]
  end
  subgraph C["Club Hub"]
    c0[Look up registration]
    f1@{ shape: fork, label: "fork" }
    c1[Mark CheckedIn]
    c2[Update attendance]
    j1@{ shape: join, label: "join" }
  end
  stop@{ shape: fr-circ, label: "end" }
  start --> o1 --> c0 --> d1
  d1 -- "[no]" --> o3 --> stop
  d1 -- "[yes]" --> f1
  f1 --> c1 --> j1
  f1 --> c2 --> j1
  j1 --> stop

Show hints

  • Order the checks in the diagram as the code will run them: find the event, then the duplicate check, then the capacity check. Cheap checks first.
  • Mermaid: an alt with several else lines is allowed; a plain else without text is fine for the success branch.
  • The fork/join shapes need Mermaid 11.3 or later (mermaid.live is always current). If your editor is older, use a node labelled "fork" and mention it in the notes.
  • A decision diamond is a question; its outgoing edges carry the answers in brackets, for example d1 -- "[no]" --> o3.

Acceptance checklist

5

State machine for Registration, implemented and tested

Hard 75 min · Developers, one commit per member

Goal

Draw the life cycle of a Registration as a state machine, implement exactly its allowed transitions in C++ (illegal transitions throw), prove it with GoogleTest, and record what this lab changed in lab06/README.md.

Steps

  1. Write lab06/state-registration.mmd (stateDiagram-v2) with the states Registered, Waitlisted, Cancelled, CheckedIn, the initial and final pseudo-states, the events register, seatFreed, cancel, checkIn, and the guards [seat free], [event full], [start - now >= 24 h].
  2. Derive lab06/state-table.md: rows = the four states, columns = seatFreed, cancel, checkIn, cells = next state or illegal. Diagram and table must agree cell by cell.
  3. Implement include/clubhub/registration_rules.hpp and src/registration_rules.cpp: enum class Trigger, toString(RegistrationStatus) and next(RegistrationStatus from, Trigger t) as a switch on from that returns the new state or throws IllegalTransition. Add the file to clubhub_core.
  4. Use next in Registration::cancel (after checking the 24-hour guard, BR-03) and Registration::checkIn, so the class cannot reach a state the diagram does not allow.
  5. Write tests/registration_rules_test.cpp: one test per legal transition (four) and at least three illegal ones with EXPECT_THROW, plus one test for the 24-hour guard. Register it with gtest_discover_tests and run ctest --test-dir build --output-on-failure.
  6. Record what changed. Create lab06/README.md from the template below: the table of this lab's files, the Lab-05 inputs reused with their commit hash (git log -1 --format=%h -- lab05/), who led each task, and one change-log row.

Starter

// include/clubhub/registration_rules.hpp
#pragma once
#include <stdexcept>
#include <string_view>
#include "clubhub/registration.hpp"

namespace clubhub {

enum class Trigger { SeatFreed, Cancel, CheckIn };

class IllegalTransition : public std::logic_error {
public:
    using std::logic_error::logic_error;
};

std::string_view toString(RegistrationStatus s);

// Returns the state after trigger t, or throws IllegalTransition
// for every arrow that is not in lab06/state-registration.mmd.
RegistrationStatus next(RegistrationStatus from, Trigger t);

} // namespace clubhub
// tests/registration_rules_test.cpp
#include <gtest/gtest.h>
#include "clubhub/registration_rules.hpp"

using clubhub::next;
using clubhub::RegistrationStatus;
using clubhub::Trigger;

TEST(RegistrationRules, WaitlistedBecomesRegisteredWhenSeatFreed) {
    EXPECT_EQ(next(RegistrationStatus::Waitlisted, Trigger::SeatFreed),
              RegistrationStatus::Registered);
}

TEST(RegistrationRules, CheckedInCannotBeCancelled) {
    EXPECT_THROW(next(RegistrationStatus::CheckedIn, Trigger::Cancel),
                 clubhub::IllegalTransition);
}

// TODO: the three other legal transitions, two more illegal ones,
// and Registration::cancel less than 24 h before the start
# Lab-06 · UML models (v1.0, 2026-10-..)

| File | Content | Led by |
|------|---------|--------|
| use-case-diagram.puml / .png | actors and goals, 1 include, 1 extend | PO |
| ... | ... | ... |

## Inputs reused
| Input | Version / commit |
|-------|------------------|
| lab05/user-stories.md | a1b2c3d |

## Change log
| Date | Change | Reason |
|------|--------|--------|
| 2026-10-.. | Lab-06 models and headers created | Lab-06 |

Expected output

$ ctest --test-dir build --output-on-failure
Test project C:/work/clubhub/build
 1/8 Test #1: RegistrationRules.WaitlistedBecomesRegisteredWhenSeatFreed ... Passed
 2/8 Test #2: RegistrationRules.WaitlistedCanBeCancelled ................ Passed
 3/8 Test #3: RegistrationRules.RegisteredCanBeCancelled ................ Passed
 4/8 Test #4: RegistrationRules.RegisteredCanCheckIn .................... Passed
 5/8 Test #5: RegistrationRules.CheckedInCannotBeCancelled .............. Passed
 6/8 Test #6: RegistrationRules.CancelledCannotCheckIn .................. Passed
 7/8 Test #7: RegistrationRules.WaitlistedCannotCheckIn ................. Passed
 8/8 Test #8: Registration.CancelLessThan24hBeforeStartThrows ........... Passed

100% tests passed, 0 tests failed out of 8

Show hints

  • Write the switch without a default: label: then GCC and Clang with -Wall warn when a new enumerator is not handled (MSVC only with the off-by-default warning C4062).
  • using enum RegistrationStatus; (C++20) inside the function lets you write Registered instead of the full name.
  • The guard belongs to the operation, the transition to the rules function: if (eventStart - now < 24h) throw RegistrationError{...}; status_ = next(status_, Trigger::Cancel);
  • If GoogleTest is not yet in your project, add it with FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.zip), FetchContent_MakeAvailable(googletest), enable_testing() and include(GoogleTest).

Acceptance checklist

Challenge: model the monthly activity report

Hard 90–120 min · optional, extra credit

Scenario

The Administrator wants, for any month, one line per approved club: number of events held, registrations, check-ins and attendance rate. Before anyone writes code, the team models the feature: how the report is computed (sequence), which new classes it needs, and where it fits in the system (component overview).

Requirements

  1. lab06/challenge/report-sequence.mmd: Administrator → CLI → ReportService::monthlyReport(year_month) → EventRepository::findByMonth and RegistrationRepository::findByEvent, with a loop fragment over the events and an opt fragment for "no events this month".
  2. lab06/challenge/report-classes.mmd: the classes the report needs (for example ReportService, MonthlyReport, ClubActivity), with attributes, multiplicities and the dependencies on the two repository interfaces.
  3. lab06/challenge/components.puml: a PlantUML component diagram with clubhub-cli, clubhub_core (domain and services), clubhub_storage (CSV repositories) and the CSV files, showing the repository interfaces as provided/required interfaces.
  4. lab06/challenge/notes.md: five to eight sentences on what the Software Engineering course will add to this picture (a web layer with a REST API, a database behind the same repository interfaces, authentication, a mobile or browser client) and which of today's components survive unchanged.

Skeleton

@startuml
' ITC Club Hub: component overview · v1.0 · 2026-10-..
component "clubhub-cli" as CLI
component "clubhub_core" as Core
component "clubhub_storage" as Storage
interface EventRepository
interface RegistrationRepository
artifact "events.csv" as EvCsv
' TODO: registrations.csv

CLI ..> Core : uses
Core --( EventRepository
Storage -- EventRepository
' TODO: the second interface, and Storage ..> the CSV artifacts
@enduml

Expected output

Monthly activity report · 2026-10
Club               Events  Registrations  Check-ins  Attendance
Robotics Club           3             74         61       82 %
Photography Club        2             40         29       73 %
English Club            0              0          0        n/a

Show hints

  • Attendance rate = check-ins / registrations that were not cancelled; decide and write down what "n/a" means when a club had no events.
  • std::chrono::year_month (C++20) is a precise type for "a month"; use it in the operation signature.
  • In PlantUML, A --( I draws a required interface (socket) and B -- I a provided one (ball).
  • A web layer would call ReportService exactly as the CLI does: that is the point of keeping the core independent of the front end.

Acceptance checklist

Grading rubric

ComponentPointsFull marks when…
Task 1 · Use case diagram10Five actors outside the boundary, goal-level use cases traced to every Must story, one include and one extend drawn correctly and justified.
Task 2 · Domain class diagram15Four classes with typed attributes, both enumerations with canonical values, multiplicities on every association end, one composition; renders without errors.
Task 3 · C++ headers20Four headers compile cleanly with the CMake project; mapping rules applied (vector, ids, optional, enum class); consistency table complete in both directions.
Task 4 · Sequence and activity20Sequence diagram with real function names, activation and a three-branch alt; activity diagram with lanes, guarded decisions and matching fork/join; declarations exist in code.
Task 5 · State machine in C++25Diagram, state table and next() agree exactly; illegal transitions throw; eight or more passing tests; README with inputs, roles and change log.
Diagram and code quality10Titles, versions and dates on every diagram; consistent names across diagrams and code; readable layout; contributions from every member visible in Git.
★ Challenge (extra credit)+20Report sequence with loop and opt, report classes, component overview with interfaces, and a clear note on what the Software Engineering course adds.
Total100 (+20)
Automatic deductions: -5 per use case that is a user-interface step ("Click Register") · -5 for a reversed «include», «extend» or generalization arrow · -3 per association end without a multiplicity · -5 for a diagram committed only as PNG without its source · -10 if the project does not build on the lab-06 branch · -5 per transition that exists in code but not in the state machine (or the reverse).

Submission

  1. Repository layout after this lab:
    clubhub/
    ├── CMakeLists.txt
    ├── include/clubhub/
    │   ├── club.hpp  event.hpp  student.hpp  registration.hpp
    │   ├── registration_rules.hpp
    │   └── registration_service.hpp  event_repository.hpp  registration_repository.hpp
    ├── src/
    │   ├── club.cpp  registration.cpp  registration_rules.cpp
    ├── tests/
    │   └── registration_rules_test.cpp
    ├── lab05/ ...
    └── lab06/
        ├── use-case-diagram.puml  use-case-diagram.png  use-case-notes.md
        ├── class-diagram.mmd  class-diagram.png  consistency.md
        ├── sequence-register.mmd  sequence-register.png
        ├── activity-checkin.mmd  activity-checkin.png
        ├── state-registration.mmd  state-registration.png  state-table.md
        ├── README.md
        └── challenge/            # optional
  2. Self-check from a clean checkout; all three commands must succeed:
    cmake -S . -B build && cmake --build build && ctest --test-dir build
    and every .mmd and .puml file opens without errors in mermaid.live or the PlantUML preview, with its PNG next to it.
  3. Push branch lab-06 and open a pull request titled Lab-06 – <team name>. In the description, list who led each task and link lab06/README.md. At least one other team member approves before merge.