Portal Backend
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.
Controllers – API Entry
Controllers are thin REST adapters and delegate all logic to Services and Assemblers.
-
Extend
BaseControllerwith generics for: Input DTO, Output DTO, Entity, Specification, ID (usuallyUUID). -
Base CRUD endpoints are provided:
GET /resource– list + filters/paginationGET /resource/{id}– detailPOST /resource– createPUT /resource/{id}– updatePATCH /resource/{id}– partial updateDELETE /resource/{id}– delete
Most controllers only define type parameters and base path. Custom endpoints are added only when required.
Assemblers – Response Construction
Assemblers build Output DTOs from entities.
-
Implement
BaseAssemblerwith a template flow:- Optional pre-processing (e.g. load lazy relations)
- Base field mapping via Mapper
- Optional enrichment (computed fields, summaries)
- Optional post-processing (links, formatting)
-
Responsibilities:
- Use Mappers for base field mapping
- Convert relations to Summary DTOs
- Optionally expose
toInputfor PATCH workflows
Assemblers isolate all response shaping logic from controllers and services.
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:
preProcessCreateInputpreProcessUpdateInput
Mapping:
postConvertToEntity
Persistence:
preSavepostSave
Queries:
preProcessQuerypostProcessQueryResult
Load/Delete:
preProcessLoad,postLoadpreProcessDelete,postDelete
Relation handling always belongs in the service layer, never in mappers.
Mappers – DTO ↔ Entity Conversion
Mappers perform field-level transformations.
-
Implement
DtoMapper:toEntitytoOutputtoInputupdateEntity
-
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.
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
EntityGraphqueries for eager loading. -
Specifications:
- Shared base specs for
id,createdAt,modifiedAt - Named-entity specs add
name,description, and aqsearch field - Entity-specific specs extend these as needed
- Shared base specs for
Controllers receive specification instances built automatically from query parameters.
Domain Model – Entities and DTOs
The domain model defines core structures:
-
Entities:
BaseEntity– id, audit fields, validation hookNamedEntity– addsname,descriptionBaseDataEntity– adds project-level fields for data entitiesAssignableEntity– 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.
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 KafkaApache KafkaA distributed event streaming platform. In CIVITAS/CORE it is used as the message bus to transport events, models and data in data flows. messages. There are two patterns:
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 KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. 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 syncConfigEventPublisherService– sends the KafkaApache KafkaA distributed event streaming platform. In CIVITAS/CORE it is used as the message bus to transport events, models and data in data flows. messagesKafkaCloudEventPublisher– handles sending and waiting for responsesKafkaConfigResultListener– receives responses from Config-Adapter
To add IDM sync to a new entity, extend EventPublishingService instead of BaseService.
Dataset Saga (DataSets)
When a datasetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. is created, updated, or deleted, multiple external systems need to be configured (FROST for IoT data, APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. for API routing, Redpanda for data pipelines). This happens asynchronously — the datasetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. is saved immediately with a PENDING status and updated to ACTIVE or FAILED once the Config-Adapter finishes.
Key classes:
DataSetSagaPublisher– sends the saga triggerDataSetSagaResultListener– receives the result and updates the datasetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions.SagaTrigger– defines the trigger types:DatasetCreate,DatasetUpdate,DatasetDelete
Running Without Kafka
KafkaApache KafkaA distributed event streaming platform. In CIVITAS/CORE it is used as the message bus to transport events, models and data in data flows. is disabled by default (kafka.enabled=false). This affects the two patterns differently:
| KafkaApache KafkaA distributed event streaming platform. In CIVITAS/CORE it is used as the message bus to transport events, models and data in data flows. enabled? | User Management Sync (Users, Groups, Roles) | DatasetDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. Saga |
|---|---|---|
| Yes | Changes are synced to KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. | DatasetsDatasetA data-related element that contains processed data and makes it available for consumption. A Dataset is populated via Pipelines and carries Metadata and access permissions. are provisioned in external systems |
| No | Changes are saved locally only (no KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. sync) | Not available |
For local development without KafkaApache KafkaA distributed event streaming platform. In CIVITAS/CORE it is used as the message bus to transport events, models and data in data flows., user/group/role operations still work but are not synced to KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs.. Set kafka.enabled=true in your profile to enable full sync.
Implementation Checklist for a New Entity
- Model
- Create entity (extend appropriate base)
- Define relations and constraints
- Add Input, Output, Summary DTOs
- Data Access
- Create repository
- Create Specification interface
- Mapping
- Implement MapStruct mapper
- Ignore relation fields; define summary mapping
- Business Logic
- Extend service
- Override hooks for relations/validation only as needed
- Response
- Implement assembler with summary generation
- API
- Create controller extending base controller
This sequence ensures all entities integrate consistently with the platform’s architecture.