Skip to content

Canteen 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 Canteen module. For system-wide context (C1 — System Context, C2 — Container), see docs/c4-architecture.md.


C3 — Component

Internal components of the Canteen module, following the flat package structure established by the sample module.

C4Component
    title Canteen Module — Component Diagram

    Container_Boundary(canteen, "Canteen Module") {
        Component(controllers, "Controllers", "@RestController", "REST endpoints for CanteenSetting, CanteenStudentsDirectory, CanteenUser, CateringService, Dashboard, FeedingList, ItemCategory, Item, ItemVariant, MealTicket, Menu, MenuSchedule, MenuSection, Order, ParentCateringService, ParentMenu, ParentOrder, Report")
        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/ CanteenSettingController, CanteenStudentsDirectoryController, CanteenUserController, CateringServiceController, DashboardController, FeedingListController, ItemCategoryController, ItemController, ItemVariantController, MealTicketController, MenuController, MenuScheduleController, MenuSectionController, OrderController, ParentCateringServiceController, ParentMenuController, ParentOrderController, ReportController
Request DTOs controllers/dto/request/ CreateCanteenSettingRequest, UpdateCanteenSettingRequest, CreateCanteenStudentsDirectoryRequest, UpdateCanteenStudentsDirectoryRequest, CreateCanteenUserRequest, UpdateCanteenUserRequest, CreateCateringServiceRequest, UpdateCateringServiceRequest, CreateDashboardRequest, UpdateDashboardRequest, CreateFeedingListRequest, UpdateFeedingListRequest, CreateItemCategoryRequest, UpdateItemCategoryRequest, CreateItemRequest, UpdateItemRequest, CreateItemVariantRequest, UpdateItemVariantRequest, CreateMealTicketRequest, UpdateMealTicketRequest, CreateMenuRequest, UpdateMenuRequest, CreateMenuScheduleRequest, UpdateMenuScheduleRequest, CreateMenuSectionRequest, UpdateMenuSectionRequest, CreateOrderRequest, UpdateOrderRequest, CreateParentCateringServiceRequest, UpdateParentCateringServiceRequest, CreateParentMenuRequest, UpdateParentMenuRequest, CreateParentOrderRequest, UpdateParentOrderRequest, CreateReportRequest, UpdateReportRequest, etc.
Response DTOs controllers/dto/response/ CanteenSettingResponse, CanteenStudentsDirectoryResponse, CanteenUserResponse, CateringServiceResponse, DashboardResponse, FeedingListResponse, ItemCategoryResponse, ItemResponse, ItemVariantResponse, MealTicketResponse, MenuResponse, MenuScheduleResponse, MenuSectionResponse, OrderResponse, ParentCateringServiceResponse, ParentMenuResponse, ParentOrderResponse, ReportResponse, etc.
Services services/ CanteenSettingService, CanteenStudentsDirectoryService, CanteenUserService, CateringServiceService, DashboardService, ExcelExportService, FeedingListService, ItemCategoryService, ItemService, ItemVariantService, MealTicketService, MenuScheduleService, MenuSectionService, MenuService, OrderService, ParentMenuService, ParentOrderService, ReportService
Repositories repositories/ CanteenOrderRepository, CanteenSettingRepository, CanteenUserRepository, CateringServiceRepository, ItemCategoryRepository, ItemRepository, ItemVariantRepository, MealTicketRepository, MenuAudienceRepository, MenuRepository, MenuScheduleRepository, MenuSectionRepository, OrderItemRepository, OrderStatusHistoryRepository, TicketPriceRepository
Entities entities/ CanteenOrder, CanteenSetting, CanteenUser, CateringService, Item, ItemCategory, ItemVariant, MealTicket, Menu, MenuAudience, MenuSchedule, MenuSection, OrderItem, OrderStatusHistory, TicketPrice
Mappers mappers/ CanteenSettingMapper, CanteenStudentsDirectoryMapper, CanteenUserMapper, CateringServiceMapper, FeedingListMapper, ItemCategoryMapper, ItemMapper, ItemVariantMapper, MealTicketMapper, MenuMapper, MenuScheduleMapper, MenuSectionMapper, OrderMapper
Event Producers events/producers/ ItemEventProducer, MealTicketEventProducer, MenuEventProducer, OrderEventProducer
Event Listeners events/listeners/ FeedingListEventListener, InvoicePaidEventListener, MenuEventListener, OrderEventListener
Constants constants/ Constants, KafkaTopics, CacheNames

C3.1 — Entity Relationship Model

The 15 core entities and their relationships.

erDiagram
    MealTicket ||--o{ CanteenOrder : "has many"
    Menu ||--o{ CanteenOrder : "has many"
    CateringService }o--o{ CanteenUser : "linked via"
    CateringService ||--o{ Item : "has many"
    ItemCategory ||--o{ Item : "has many"
    Item ||--o{ ItemVariant : "has many"
    Menu ||--o{ MealTicket : "has many"
    Menu }o--o{ MenuAudience : "linked via"
    MenuSection ||--o{ MenuSchedule : "has many"
    Item ||--o{ MenuSchedule : "has many"
    Menu ||--o{ MenuSection : "has many"
    Item ||--o{ OrderItem : "has many"
    MenuSection ||--o{ OrderItem : "has many"
    MealTicket }o--o{ TicketPrice : "linked via"

    CanteenOrder {
        UUID id PK
        String orderNumber
        UUID studentId FK
        UUID mealTicketId FK
        OrderType orderType "TICKET, PRE_ORDER, POS"
        BigDecimal totalPrice
        PaymentMode paymentMode "SIKA_ID, MOBILE_MONEY, CARD, CREDIT, CASH, ALLOCATION"
        OrderStatus status "DRAFT, PENDING_PAYMENT, PAID, SERVED, PARTIALLY_SERVED, CANCELLED"
        LocalDateTime scheduledDate
        UUID batchId FK
        UUID menuId FK
        UUID createdBy
        Instant redeemedAt
        Instant cancelledAt
        UUID cancelledBy
        UUID schoolId FK
        String invoiceId
        BigDecimal serviceFee
        Instant paidAt
        Instant createdAt
        Instant updatedAt
    }

    CanteenSetting {
        UUID id PK
        RedemptionMode redemptionMode "AUTO_REDEEM, AUTO_REDEEM_PRESENT, SCAN_TO_REDEEM"
        UUID schoolId FK
        Instant createdAt
        Instant updatedAt
    }

    CanteenUser {
        UUID id PK
        UUID userId FK
        UUID cateringServiceId FK
        CanteenRole role "CANTEEN_ADMIN, CANTEEN_MANAGER, POS_OPERATOR"
        boolean active
        UUID schoolId FK
        Instant createdAt
        Instant updatedAt
    }

    CateringService {
        UUID id PK
        String name
        String logo
        String email
        String phone
        UUID schoolId FK
        SettlementAccountType settlementAccountType "BANK, MOBILE_MONEY"
        String settlementAccountName
        String settlementAccountNumber
        GhanaBank settlementBank "ABSA_BANK_GHANA, ACCESS_BANK_GHANA, AGRICULTURAL_DEVELOPMENT_BANK, BANK_OF_AFRICA_GHANA, CALBANK, CONSOLIDATED_BANK_GHANA, ECOBANK_GHANA, FBN_BANK_GHANA, FIDELITY_BANK_GHANA, FIRST_ATLANTIC_BANK, FIRST_NATIONAL_BANK_GHANA, GCB_BANK, GUARANTY_TRUST_BANK_GHANA, NATIONAL_INVESTMENT_BANK, OMNIBSIC_BANK_GHANA, PRUDENTIAL_BANK, REPUBLIC_BANK_GHANA, SOCIETE_GENERALE_GHANA, STANBIC_BANK_GHANA, STANDARD_CHARTERED_BANK_GHANA, UNITED_BANK_FOR_AFRICA_GHANA, UNIVERSAL_MERCHANT_BANK, ZENITH_BANK_GHANA"
        MobileMoneyProvider settlementMobileMoneyProvider "MTN, TELECEL, AIRTELTIGO"
        BigDecimal platformServiceFeeOverride
        Instant createdAt
        Instant updatedAt
    }

    Item {
        UUID id PK
        UUID cateringServiceId FK
        UUID itemCategoryId FK
        String name
        String image
        String description
        String allergens
        int maxQuantityPerDay
        PricingStyle pricingStyle "FIXED, VARIANT, NO_PRICE"
        BigDecimal defaultPrice
        String currency
        UUID schoolId FK
        UUID createdBy
        Instant createdAt
        Instant updatedAt
    }

    ItemCategory {
        UUID id PK
        String name
        String description
        String themeColor
        UUID schoolId FK
        Instant createdAt
        Instant updatedAt
    }

    ItemVariant {
        UUID id PK
        UUID itemId FK
        String variantName
        BigDecimal price
        AudienceScope audience "ALL, SPECIFIC"
        List targetCampusIds
        List targetClassroomIds
        Instant createdAt
        Instant updatedAt
    }

    MealTicket {
        UUID id PK
        UUID menuId FK
        String name
        UUID academicTermId FK
        PricingMethod pricingMethod "DAILY, MONTHLY, TERMLY"
        PricingScheme pricingScheme "UNIFORM, DIFFERENTIAL"
        BigDecimal uniformPrice
        boolean active
        UUID schoolId FK
        Instant createdAt
        Instant updatedAt
    }

    Menu {
        UUID id PK
        String name
        String description
        MenuPurpose purpose "INFO_ONLY, PARENTS_CAN_ORDER"
        PaymentType paymentType "ORDER_AND_PAY_ITEM_PRICING, ORDER_ONLY_NO_PAYMENT, ORDER_AND_PAY_TICKET_PRICING"
        LocalDate startDate
        LocalDate endDate
        DeadlineType orderingDeadlineType "DAILY, WEEKLY"
        LocalTime dailyCutoffTime
        int dailyCutoffDays
        DayOfWeek weeklyCutoffDay "MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY"
        LocalTime weeklyCutoffTime
        int orderCancellationHours
        boolean published
        AudienceScope audience "ALL, SPECIFIC"
        List targetCampusIds
        List targetSectionIds
        List targetClassroomIds
        List excludedStudentIds
        int visibilityDays
        UUID schoolId FK
        UUID updatedBy
        String updatedByName
        Instant createdAt
        Instant updatedAt
    }

    MenuAudience {
        UUID id PK
        UUID menuId FK
        UUID campusId FK
        UUID classroomId FK
        Instant createdAt
        Instant updatedAt
    }

    MenuSchedule {
        UUID id PK
        UUID menuSectionId FK
        UUID itemId FK
        LocalDate date
        UUID scheduledViaCategoryId FK
        int stockAvailable
        SchedulePattern recurrenceRule "SINGLE_DAY, EVERY_DAY, EVERY_WEEKDAY, SPECIFIC_DAYS"
        Instant createdAt
        Instant updatedAt
    }

    MenuSection {
        UUID id PK
        UUID menuId FK
        String name
        LocalTime servingStartTime
        LocalTime servingEndTime
        SelectionMode selectionMode "SINGLE, MULTIPLE"
        String themeColor
        Instant createdAt
        Instant updatedAt
    }

    OrderItem {
        UUID id PK
        UUID orderId FK
        UUID itemId FK
        UUID variantId FK
        UUID menuSectionId FK
        int quantity
        BigDecimal unitPrice
        boolean served
        Instant servedAt
        long servedBy
        String servedByName
        Instant createdAt
        Instant updatedAt
    }

    OrderStatusHistory {
        UUID id PK
        UUID orderId FK
        OrderStatus fromStatus "DRAFT, PENDING_PAYMENT, PAID, SERVED, PARTIALLY_SERVED, CANCELLED"
        OrderStatus toStatus "DRAFT, PENDING_PAYMENT, PAID, SERVED, PARTIALLY_SERVED, CANCELLED"
        UUID changedBy
        Instant changedAt
    }

    TicketPrice {
        UUID id PK
        UUID mealTicketId FK
        UUID targetGroupId FK
        BigDecimal price
        Instant createdAt
        Instant updatedAt
    }

Cross-module references

Field Source module Purpose
CanteenOrder.studentId School Links to Student
CanteenOrder.schoolId School Links to School
CanteenSetting.schoolId School Links to School
CanteenUser.schoolId School Links to School
CateringService.schoolId School Links to School
Item.schoolId School Links to School
ItemCategory.schoolId School Links to School
MealTicket.schoolId School Links to School
Menu.schoolId School Links to School
MenuAudience.campusId School Links to Campus
MenuAudience.classroomId School Links to Classroom

C3.2 — REST API Structure

All endpoints follow the pattern established by the sample module.

Entity Base path Operations
CanteenSetting /api/canteen/settings GET (list, paginated), PUT /{id}
CanteenStudentsDirectory /api/canteen/students GET (list, paginated)
CanteenUser /api/canteen/users GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id}
CateringService /api/canteen/catering-services GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id}
Dashboard /api/canteen/dashboard GET (list, paginated), GET /export
FeedingList /api/canteen/feeding-list GET (list, paginated), PUT /bulk-redeem, GET /export
ItemCategory /api/canteen/item-categories GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id}
Item /api/canteen/items GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id}, GET /library, GET /recently-used
ItemVariant /api/canteen/items/{itemId}/variants GET (list, paginated), POST, DELETE /{variantId}, PUT /{variantId}
MealTicket /api/canteen/meal-tickets GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id}
Menu /api/canteen/menus GET (list, paginated), GET /{id}, POST, PUT /{id}, DELETE /{id}, PUT /{id}/publish, GET /{id}/schedule, PUT /{id}/unpublish
MenuSchedule /api/canteen/menus/{menuId}/sections/{sectionId}/schedules GET (list, paginated), POST, DELETE `, PATCH/bulk-stock, DELETE/{scheduleId}, PATCH/{scheduleId}`
MenuSection /api/canteen/menus/{menuId}/sections GET (list, paginated), POST, DELETE /{sectionId}, PUT /{sectionId}
Order /api/canteen/orders GET (list, paginated), GET /{id}, POST, PUT /{id}/cancel, PUT /{id}/items/{orderItemId}/serve, PUT /{id}/redeem
ParentCateringService `` GET /api/canteen/parent/catering-services, GET /api/canteen/parent/catering-services/{cateringServiceId}/schedule
ParentMenu `` GET /api/canteen/parent/children/{studentId}/menus, GET /api/canteen/parent/children/{studentId}/menus/{menuId}/schedule, GET /api/canteen/parent/kids-schedule
ParentOrder `` GET /api/canteen/parent/orders, POST /api/canteen/parent/orders, GET /api/canteen/parent/orders/batch/{batchId}, PUT /api/canteen/parent/orders/batch/{batchId}/checkout, DELETE /api/canteen/parent/orders/{orderId}, GET /api/canteen/parent/orders/{orderId}, PUT /api/canteen/parent/orders/{orderId}, PUT /api/canteen/parent/orders/{orderId}/cancel
Report /api/canteen/reports GET /orders, GET /orders/export, GET /sales, GET /sales/export, GET /sika-id, GET /sika-id/export, GET /tickets, GET /tickets/export

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/>canteen.*"]
    Kafka -->|consume| Listener

    subgraph Topics
        T1["canteen.orders"]
        T2["canteen.menus"]
        T3["canteen.meal-tickets"]
        T4["canteen.items"]
        T5["canteen.feeding-list"]
    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
menuSectionById entity UUID findById, update, create delete
cateringServiceById entity UUID findById, update, create delete
mealTicketById entity UUID findById, update, create delete
itemCategoryById entity UUID findById, update, create delete
itemById entity UUID findById, update, create delete
canteenSettingById entity UUID update ``
menuById entity UUID unpublish, findById, update, create, publish 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 @Autowired fields)
  • 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)

Document Path
Sample module guide Sample Guide
Root PRD index docs/PRD.md
Backend system README backend/README.md