School Module — C3/C4 Architecture¶
Auto-generated by c4docgen on 2026-04-27. Do not edit manually — regenerate with
./gradlew generateC4Docs.
Component and code-level architecture for the School module. For system-wide context (C1 — System Context, C2 — Container), see docs/c4-architecture.md.
C3 — Component¶
Internal components of the School module, following the flat package structure established by the sample module.
C4Component
title School Module — Component Diagram
Container_Boundary(school, "School Module") {
Component(controllers, "Controllers", "@RestController", "REST endpoints for AcademicHoliday, AcademicPeriod, AcademicYear, Campus, Classroom, Family, Guardian, MySchool, MyStaffProfile, ParentChildren, School, Section, Staff, Student")
Component(services, "Services", "@Service", "Business logic, caching, event publishing")
Component(repositories, "Repositories", "JpaRepository", "Data access interfaces")
Component(mappers, "Mappers", "@Component", "Entity ↔ DTO conversion")
Component(producers, "Event Producers", "@Component", "Publishes domain events to Kafka")
Component(listeners, "Event Listeners", "@KafkaListener", "Consumes domain events from Kafka")
Component(entities, "Entities", "@Entity", "JPA entity classes")
ComponentDb(dtos, "DTOs", "Java Records", "Request/response data transfer objects")
Component(constants, "Constants", "static final", "API paths, Kafka topics, cache names")
}
Rel(controllers, services, "Delegates to")
Rel(controllers, mappers, "Converts via")
Rel(services, repositories, "Persists via")
Rel(services, mappers, "Converts via")
Rel(services, producers, "Emits events via")
Rel(listeners, services, "May trigger")
Rel(repositories, entities, "Manages")
Component → Package mapping¶
| Component | Package | Key classes |
|---|---|---|
| Controllers | controllers/ |
AcademicHolidayController, AcademicPeriodController, AcademicYearController, CampusController, ClassroomController, FamilyController, GuardianController, MySchoolController, MyStaffProfileController, ParentChildrenController, SchoolController, SectionController, StaffController, StudentController |
| Request DTOs | controllers/dto/request/ |
CreateAcademicHolidayRequest, UpdateAcademicHolidayRequest, CreateAcademicPeriodRequest, UpdateAcademicPeriodRequest, CreateAcademicYearRequest, UpdateAcademicYearRequest, CreateCampusRequest, UpdateCampusRequest, CreateClassroomRequest, UpdateClassroomRequest, CreateFamilyRequest, UpdateFamilyRequest, CreateGuardianRequest, UpdateGuardianRequest, CreateMySchoolRequest, UpdateMySchoolRequest, CreateMyStaffProfileRequest, UpdateMyStaffProfileRequest, CreateParentChildrenRequest, UpdateParentChildrenRequest, CreateSchoolRequest, UpdateSchoolRequest, CreateSectionRequest, UpdateSectionRequest, CreateStaffRequest, UpdateStaffRequest, CreateStudentRequest, UpdateStudentRequest, etc. |
| Response DTOs | controllers/dto/response/ |
AcademicHolidayResponse, AcademicPeriodResponse, AcademicYearResponse, CampusResponse, ClassroomResponse, FamilyResponse, GuardianResponse, MySchoolResponse, MyStaffProfileResponse, ParentChildrenResponse, SchoolResponse, SectionResponse, StaffResponse, StudentResponse, etc. |
| Services | services/ |
AcademicHolidayService, AcademicPeriodService, AcademicYearService, CampusService, ClassroomService, FamilyService, GuardianService, ParentChildrenService, SchoolLegacyImportService, SchoolService, SectionService, StaffService, StudentService |
| Repositories | repositories/ |
AcademicHolidayRepository, AcademicPeriodRepository, AcademicYearRepository, CampusRepository, ClassroomRepository, FamilyRepository, GuardianRepository, SchoolRepository, SchoolStaffRepository, SectionRepository, StudentGuardianRepository, StudentRepository |
| Entities | entities/ |
AcademicHoliday, AcademicPeriod, AcademicYear, Campus, Classroom, Family, Guardian, School, SchoolStaff, Section, Student, StudentGuardian |
| Mappers | mappers/ |
AcademicHolidayMapper, AcademicPeriodMapper, AcademicYearMapper, CampusMapper, ClassroomMapper, FamilyMapper, GuardianMapper, SchoolMapper, SectionMapper, StaffMapper, StudentMapper |
| Event Producers | events/producers/ |
AcademicHolidayEventProducer, AcademicPeriodEventProducer, AcademicYearEventProducer, CampusEventProducer, ClassroomEventProducer, FamilyEventProducer, GuardianEventProducer, SchoolEventProducer, SectionEventProducer, StaffEventProducer, StudentEventProducer |
| Event Listeners | events/listeners/ |
AcademicHolidayEventListener, AcademicPeriodEventListener, AcademicYearEventListener, CampusEventListener, ClassroomEventListener, FamilyEventListener, GuardianEventListener, SchoolEventListener, SectionEventListener, StaffEventListener, StudentEventListener |
| Constants | constants/ |
Constants, KafkaTopics, CacheNames |
C3.1 — Entity Relationship Model¶
The 12 core entities and their relationships.
erDiagram
AcademicYear ||--o{ AcademicHoliday : "has many"
AcademicPeriod ||--o{ AcademicHoliday : "has many"
AcademicYear ||--o{ AcademicPeriod : "has many"
School ||--o{ AcademicYear : "has many"
Campus ||--o{ AcademicYear : "has many"
Section ||--o{ AcademicYear : "has many"
School ||--o{ Campus : "has many"
Section ||--o{ Classroom : "has many"
School ||--o{ Family : "has many"
Family ||--o{ Guardian : "has many"
School ||--o{ SchoolStaff : "has many"
Campus ||--o{ SchoolStaff : "has many"
Campus ||--o{ Section : "has many"
School ||--o{ Student : "has many"
Classroom ||--o{ Student : "has many"
Family ||--o{ Student : "has many"
Student ||--o{ StudentGuardian : "has many"
Guardian ||--o{ StudentGuardian : "has many"
AcademicHoliday {
UUID id PK
UUID academicYearId FK
UUID academicPeriodId FK
String name
LocalDate startDate
LocalDate endDate
String description
Instant createdAt
Instant updatedAt
long legacyHolidayId
}
AcademicPeriod {
UUID id PK
UUID academicYearId FK
String name
LocalDate startDate
LocalDate endDate
Instant createdAt
Instant updatedAt
long legacyPeriodId
}
AcademicYear {
UUID id PK
UUID schoolId FK
UUID campusId FK
UUID sectionId FK
String name
LocalDate startDate
LocalDate endDate
AcademicPeriodType periodType "TERM, SEMESTER, QUARTER, TRIMESTER"
boolean isActive
Instant createdAt
Instant updatedAt
long legacyYearId
}
Campus {
UUID id PK
UUID schoolId FK
String name
String code
String address
String phone
String email
String principalName
boolean isActive
Instant createdAt
Instant updatedAt
long legacyCampusId
Map legacyConfiguration
}
Classroom {
UUID id PK
UUID sectionId FK
String name
String code
String academicYear
int capacity
UUID classTeacherId FK
String roomNumber
boolean isActive
Instant createdAt
Instant updatedAt
long legacyGradeId
}
Family {
UUID id PK
UUID schoolId FK
String familyName
String familyCode
String primaryAddress
String primaryPhone
String primaryEmail
String notes
boolean isActive
Instant createdAt
Instant updatedAt
}
Guardian {
UUID id PK
UUID familyId FK
GuardianType guardianType "FATHER, MOTHER, STEPFATHER, STEPMOTHER, GRANDFATHER, GRANDMOTHER, UNCLE, AUNT, OLDER_SIBLING, LEGAL_GUARDIAN, FOSTER_PARENT, RELATIVE, FAMILY_FRIEND, OTHER"
String firstName
String middleName
String lastName
String email
String phonePrimary
String phoneSecondary
String occupation
String employer
String address
String nationalId
String profilePhotoUrl
boolean isEmergencyContact
boolean canPickupStudent
boolean isPrimaryContact
long legacyUserId
boolean isActive
Instant createdAt
Instant updatedAt
}
School {
UUID id PK
String name
String code
String logoUrl
String address
String phone
String email
String website
String accreditationNumber
String defaultCurrency
long legacySchoolId
boolean isActive
Instant createdAt
Instant updatedAt
}
SchoolStaff {
UUID id PK
UUID schoolId FK
UUID campusId FK
String staffIdNumber
String firstName
String middleName
String lastName
String email
String phone
LocalDate dateOfBirth
Gender gender "MALE, FEMALE, OTHER"
String profilePhotoUrl
StaffRole role "TEACHER, ADMINISTRATOR, HEADMASTER, SUPPORT, OTHER"
String department
EmploymentType employmentType "FULL_TIME, PART_TIME, CONTRACT"
LocalDate hireDate
StaffStatus status "ACTIVE, INACTIVE, ON_LEAVE, TERMINATED"
long legacyUserId
Instant createdAt
Instant updatedAt
}
Section {
UUID id PK
UUID campusId FK
String name
String code
int levelOrder
String description
boolean isActive
Instant createdAt
Instant updatedAt
}
Student {
UUID id PK
UUID schoolId FK
UUID classroomId FK
UUID familyId FK
String studentIdNumber
String firstName
String middleName
String lastName
LocalDate dateOfBirth
Gender gender "MALE, FEMALE, OTHER"
String profilePhotoUrl
String nationality
String religion
String bloodGroup
String medicalNotes
LocalDate enrollmentDate
StudentStatus status "ACTIVE, INACTIVE, GRADUATED, WITHDRAWN, SUSPENDED"
Instant createdAt
Instant updatedAt
long legacyStudentId
}
StudentGuardian {
UUID studentId PK
UUID guardianId PK
boolean isPrimaryContact
boolean isEmergencyContact
boolean canPickupStudent
String relationshipNotes
}
C3.2 — REST API Structure¶
All endpoints follow the pattern established by the sample module.
| Entity | Base path | Operations |
|---|---|---|
| AcademicHoliday | /api/school/academic-holidays |
GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id} |
| AcademicPeriod | /api/school/academic-periods |
GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id} |
| AcademicYear | /api/school/academic-years |
GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id}, GET /resolve |
| Campus | /api/school/campuses |
GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id} |
| Classroom | /api/school/classrooms |
GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id} |
| Family | /api/school/families |
GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id} |
| Guardian | /api/school/guardians |
GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id} |
| MySchool | /api/school/my-school |
GET (list, paginated) |
| MyStaffProfile | /api/school/my-staff-profile |
GET (list, paginated) |
| ParentChildren | /api/school/parent/children |
GET (list, paginated) |
| School | /api/school/schools |
GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id} |
| Section | /api/school/sections |
GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id} |
| Staff | /api/school/staff |
GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id} |
| Student | /api/school/students |
GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id} |
All list endpoints support page (default: 0) and size (default: 20) query parameters, returning PagedResponse<T> from modules/shared/.
C3.3 — Event Flow¶
Domain events are published to Kafka on key state changes.
flowchart LR
Service -->|publish| Producer
Producer -->|send| Kafka["Kafka Topic<br/>school.*"]
Kafka -->|consume| Listener
subgraph Topics
T1["school.schools"]
T2["school.campuses"]
T3["school.sections"]
T4["school.classrooms"]
T5["school.students"]
T6["school.families"]
T7["school.staff"]
T8["school.guardians"]
T9["school.academic-years"]
T10["school.academic-periods"]
T11["school.academic-holidays"]
end
Event payload structure (Java record):
*Event {
type: EventType (CREATED, UPDATED, DELETED)
id: UUID
... entity-specific fields ...
timestamp: Instant
}
C3.4 — Caching Strategy¶
Redis caching at the service layer, following the sample module pattern.
| Cache name | Key | Populated by | Evicted by |
|---|---|---|---|
academicPeriodById |
entity UUID | findById, update, create |
delete |
familyById |
entity UUID | findById, update, create |
delete |
studentById |
entity UUID | findById, update, create |
delete |
campusById |
entity UUID | findById, update, create |
delete |
academicYearById |
entity UUID | findById, update, create |
delete |
guardianById |
entity UUID | findById, update, create |
delete |
schoolByLegacyId |
entity UUID | findByLegacySchoolId |
`` |
schoolById |
entity UUID | findById, update, create |
delete |
academicHolidayById |
entity UUID | findById, update, create |
delete |
staffById |
entity UUID | findById, update, create |
delete |
sectionById |
entity UUID | findById, update, create |
delete |
classroomById |
entity UUID | findById, update, create |
delete |
Annotations: @Cacheable (read), @CachePut (write), @CacheEvict (delete).
C4 — Code Patterns¶
Implementation follows the conventions from the sample module.
Request flow¶
HTTP Request
→ Controller (validates, delegates)
→ Mapper.toEntity(request)
→ Service (business logic)
→ Repository.save(entity)
→ Mapper.toEvent(entity, CREATED)
→ EventProducer.publish(event)
→ Redis cache updated
→ Mapper.toResponse(entity)
→ HTTP Response (JSON)
Shared utilities (reuse from modules/shared/)¶
| Class | Usage |
|---|---|
PagedResponse<T> |
Wrap all paginated list responses |
ResourceNotFoundException |
Throw on entity-not-found (auto-mapped to 404 ProblemDetail) |
Conventions¶
- All DTOs are immutable Java records
- Constructor injection only (no
@Autowiredfields) - OpenAPI annotations on all controllers and DTOs (
@Tag,@Operation,@Schema) - Constants for API paths, Kafka topics, and cache names (no magic strings)
- Integration tests with Testcontainers (real Postgres, Kafka, Redis)
Related documents¶
| Document | Path |
|---|---|
| Sample module guide | Sample Guide |
| Root PRD index | docs/PRD.md |
| Backend system README | backend/README.md |