Skip to content

Contributing to Smartsapp

Guide for adding new backend modules to the Spring Boot system.


Prerequisites

  • Java 21+
  • Podman (or Docker) — for local infrastructure and tests
  • Git hooks — run once after cloning:
    git config core.hooksPath .githooks
    

1. Decide where your module lives

Category Path What goes here Examples
Customer modules/customer/<name>/ Feature modules — business logic for end users canteen, school, student attendance, invoice
Platform modules/platform/<name>/ Wrappers around OSS tooling that support the app auth (Keycloak), chat, notifications (Novu), wallet (Formance)
Shared modules/shared/ Cross-cutting utilities used by all modules PagedResponse, ResourceNotFoundException

Package names cannot contain hyphens: academic-reportacademicreport.

2. Documentation first

Complete these before writing code.

Product Requirements (PRD)

  1. Get the module PRD from the product team (or write one). Save it as a PDF in the module's docs folder:
    docs/modules/<name>/<Name>_PRD.pdf
    
  2. Add a link to docs/PRD.md in the module index table.

If the PRD already exists, review it with the product team and update if needed. Do not start implementation against an outdated PRD.

C4 Architecture

Create a C4 architecture doc in the module's docs folder:

docs/modules/<name>/<name>-c4-architecture.md

Include (using Mermaid diagrams): - C3 Component diagram — controllers, services, repositories, events - C3.1 Entity Relationship Model — database schema - C3.2 REST API structure — endpoints, HTTP methods, pagination - C3.3 Event flow — Kafka topics, event payloads - C3.4 Caching strategy — Redis annotations and cache names

Use an existing module's C4 doc as a template:

docs/modules/school/school-c4-architecture.md

AI-assisted generation: After creating the PRD, prompt your AI agent:

Generate C4 architecture docs for the [module] module based on the PRD, following the school module's C4 doc as a template.

Then update the system-level docs/c4-architecture.md to include the new module in the C2 container diagram.

3. Implement the module

Directory structure

Create the package tree under modules/customer/<name>/ (or platform/<name>/):

<name>/
├── config/           Spring @Configuration beans
├── constants/        Static final values (API paths, Kafka topics, cache names)
├── controllers/      REST controllers
│   └── dto/
│       ├── request/  Inbound request bodies (records)
│       └── response/ Outbound response bodies (records)
├── entities/         JPA entities and enums
├── events/
│   ├── listeners/    @KafkaListener consumers
│   ├── messages/     Event payload records
│   └── producers/    KafkaTemplate publishers
├── mappers/          Entity <-> DTO conversion (@Component)
├── repositories/     Spring Data JPA interfaces
└── services/         Business logic (@Service)

See backend/src/main/java/com/smartsapp/system/modules/sample/README.md for a complete working reference.

Naming conventions

Layer Pattern Example
Entities ClassName Campus, Item
Repositories *Repository CampusRepository
Services *Service CampusService
Controllers *Controller CampusController
Request DTOs Create*Request / Update*Request CreateCampusRequest
Response DTOs *Response CampusResponse
Event payloads *Event CampusEvent
Producers *EventProducer CampusEventProducer
Listeners *EventListener CampusEventListener

Register with OpenAPI

Add a GroupedOpenApi bean in backend/src/main/java/com/smartsapp/system/config/OpenApiConfig.java:

@Bean
public GroupedOpenApi customerNewModuleGroup() {
    return GroupedOpenApi.builder()
        .group("customer-newmodule")
        .displayName("Customer – New Module")
        .packagesToScan("com.smartsapp.system.modules.customer.newmodule.controllers")
        .build();
}

Architecture rules

These are enforced by ArchUnit — violations fail the build:

  • Cross-module imports: Only services and entities from other modules. Everything else (controllers, repositories, events, mappers, config) is module-private.
  • Shared module: Always importable from any module.

Other conventions: - Constructor injection — no @Autowired on fields - DTOs as Java records (immutable, no boilerplate) - Use PagedResponse<T> from shared.dto for paginated endpoints - Throw ResourceNotFoundException from shared.exceptions for 404s - Define API paths, Kafka topics, and cache names in constants/

Kafka setup

If your module produces or consumes events:

  1. Define topic names in constants/KafkaTopics.java
  2. Add your events package to the trusted packages in application.yml:
    spring.json.trusted.packages: ...,com.smartsapp.system.modules.customer.<name>.events.messages
    

4. Add integration tests

Tests use real infrastructure via Testcontainers — no mocking.

Extend the shared base class

class NewModuleControllerTest extends IntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private NewModuleRepository repository;

    @BeforeEach
    void setUp() {
        repository.deleteAll();
    }

    @Test
    void list_returnsPaginatedResults() throws Exception {
        // ...
    }
}

IntegrationTest provides shared Postgres, Kafka, and Redis containers that start once for the entire test suite. Do not define your own @Container fields.

AI-assisted test generation

After implementing your module, prompt your AI agent:

Add integration tests for the [module] controller extending IntegrationTest, following the TodoControllerTest pattern.

Coverage requirement

JaCoCo enforces 85% minimum line coverage. The build fails if coverage drops below this threshold.

View the coverage report after running tests:

open build/reports/jacoco/test/html/index.html

5. Commit and push

Follow Conventional Commits format (enforced by git hook):

feat(school): add campus CRUD endpoints
fix(canteen): handle null menu item price
docs(school): add C4 architecture doc
test(school): add campus controller integration tests

The pre-commit hook automatically runs tests when backend files are staged.

Checklist

Before opening a PR, verify:

  • [ ] Module PRD exists in docs/modules/<name>/ and is linked in docs/PRD.md
  • [ ] C4 architecture doc created in docs/modules/<name>/
  • [ ] System C4 (docs/c4-architecture.md) updated with new module
  • [ ] Module follows the sample module directory structure
  • [ ] OpenApiConfig.java updated with new GroupedOpenApi bean
  • [ ] Kafka trusted packages updated in application.yml (if using events)
  • [ ] Integration tests extend IntegrationTest (no per-class containers)
  • [ ] Test coverage >= 85%
  • [ ] All tests pass locally (./gradlew test)
  • [ ] Commits follow Conventional Commits format