Skip to main content
Version: 2.0-beta

Developer Guide


1. Purpose

This guide explains how to implement and extend the Portal Backend at a code-level. It complements the Architectural Overview and describes how Controllers, Services, Assemblers, Mappers, Repositories, and the Domain Model work together.


2. Controllers – API Entry

Controllers are thin REST adapters and delegate all logic to Services and Assemblers.

  • Extend BaseController with generics for: Input DTO, Output DTO, Entity, Specification, ID (usually UUID).

  • Base CRUD endpoints are provided:

    • GET /resource – list + filters/pagination
    • GET /resource/{id} – detail
    • POST /resource – create
    • PUT /resource/{id} – update
    • PATCH /resource/{id} – partial update
    • DELETE /resource/{id} – delete

Most controllers only define type parameters and base path. Custom endpoints are added only when required.


3. Assemblers – Response Construction

Assemblers build Output DTOs from entities.

  • Implement BaseAssembler with a template flow:

    1. Optional pre-processing (e.g. load lazy relations)
    2. Base field mapping via Mapper
    3. Optional enrichment (computed fields, summaries)
    4. Optional post-processing (links, formatting)
  • Responsibilities:

    • Use Mappers for base field mapping
    • Convert relations to Summary DTOs
    • Optionally expose toInput for PATCH workflows

Assemblers isolate all response shaping logic from controllers and services.


4. Services – Business Logic and Hooks

Services orchestrate repositories, transactions, and domain rules.

  • Extend BaseService, which provides all standard operations.
  • Override lifecycle hooks only when needed:

Input:

  • preProcessCreateInput
  • preProcessUpdateInput

Mapping:

  • postConvertToEntity

Persistence:

  • preSave
  • postSave

Queries:

  • preProcessQuery
  • postProcessQueryResult

Load/Delete:

  • preProcessLoad, postLoad
  • preProcessDelete, postDelete

Relation handling always belongs in the service layer, never in mappers.


5. Mappers – DTO ↔ Entity Conversion

Mappers perform field-level transformations.

  • Implement DtoMapper:

    • toEntity
    • toOutput
    • toInput
    • updateEntity
  • MapStruct generates implementations:

    • Relation fields are ignored
    • Create ignores nulls for optional fields
    • Update/PATCH applies explicit nulls

Mappers focus purely on type-safe field copying.


6. Repositories and Specifications – Data Access

Repositories encapsulate all DB operations.

  • Base repositories build on Spring Data JPA and support CRUD + Specifications.

  • Entity-specific repositories extend the base and may define EntityGraph queries for eager loading.

  • Specifications:

    • Shared base specs for id, createdAt, modifiedAt
    • Named-entity specs add name, description, and a q search field
    • Entity-specific specs extend these as needed

Controllers receive specification instances built automatically from query parameters.


7. Domain Model – Entities and DTOs

The domain model defines core structures:

  • Entities:

    • BaseEntity – id, audit fields, validation hook
    • NamedEntity – adds name, description
    • BaseDataEntity – adds project-level fields for data entities
    • AssignableEntity – adds role/group assignment support
    • Concrete entities extend these and define relations and additional fields
  • DTOs:

    • Input DTOs – client data, relations as IDs
    • Output DTOs – full responses with nested summaries
    • Summary DTOs – minimal representations for nested relations

Validation occurs at the DTO, service, and optional entity level.


8. Event Communication with Config-Adapter

When certain entities are created, updated, or deleted, the Portal Backend needs to tell external systems about these changes. This happens via Kafka messages. There are two patterns:

8.1 User Management Sync (Users, Groups, Roles)

When a user, group, or role is saved, the backend sends a message to the Config-Adapter and waits for a response before saving to the database. The Config-Adapter applies the change in Keycloak and replies with success or failure.

If the Config-Adapter does not respond within 10 seconds, the operation is rolled back.

Key classes:

  • EventPublishingService – base service for entities that need external sync
  • ConfigEventPublisherService – sends the Kafka messages
  • KafkaCloudEventPublisher – handles sending and waiting for responses
  • KafkaConfigResultListener – receives responses from Config-Adapter

To add IDM sync to a new entity, extend EventPublishingService instead of BaseService.

8.2 Dataset Saga (DataSets)

When a dataset is created, updated, or deleted, multiple external systems need to be configured (FROST for IoT data, APISIX for API routing, Redpanda for data pipelines). This happens asynchronously — the dataset is saved immediately with a PENDING status and updated to ACTIVE or FAILED once the Config-Adapter finishes.

Key classes:

  • DataSetSagaPublisher – sends the saga trigger
  • DataSetSagaResultListener – receives the result and updates the dataset
  • SagaTrigger – defines the trigger types: DatasetCreate, DatasetUpdate, DatasetDelete

8.3 Running Without Kafka

Kafka is disabled by default (kafka.enabled=false). This affects the two patterns differently:

Kafka enabled?User Management Sync (Users, Groups, Roles)Dataset Saga
YesChanges are synced to KeycloakDatasets are provisioned in external systems
NoChanges are saved locally only (no Keycloak sync)Not available

For local development without Kafka, user/group/role operations still work but are not synced to Keycloak. Set kafka.enabled=true in your profile to enable full sync.


9. Implementation Checklist for a New Entity

  1. Model
  • Create entity (extend appropriate base)
  • Define relations and constraints
  • Add Input, Output, Summary DTOs
  1. Data Access
  • Create repository
  • Create Specification interface
  1. Mapping
  • Implement MapStruct mapper
  • Ignore relation fields; define summary mapping
  1. Business Logic
  • Extend service
  • Override hooks for relations/validation only as needed
  1. Response
  • Implement assembler with summary generation
  1. API
  • Create controller extending base controller

This sequence ensures all entities integrate consistently with the platform’s architecture.