Skip to content
Back to skills

Banking Domain Conventions

ASecurity

Use when writing code in a Firefly Banking Platform (firefly-oss) service — applies domain organization, service taxonomy, inter-service communication patterns, POM inheritance, and platform-specific conventions

  • 57 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 17, 2026
businessgojavasqlreactrailsspringtestingapidatabasefrontend

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 17, 2026

npx -y skills add diegosouzapw/awesome-omni-skill --skill banking-domain-conventions --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Banking Domain Conventions?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Banking Domain Conventions
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/diegosouzapw-banking-domain-conventions/badge)](https://www.skillsdirectory.com/skills/diegosouzapw-banking-domain-conventions)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: banking-domain-conventions
description: Use when writing code in a Firefly Banking Platform (firefly-oss) service — applies domain organization, service taxonomy, inter-service communication patterns, POM inheritance, and platform-specific conventions
---

# Firefly Banking Platform Conventions

## 1. Service Taxonomy

The Firefly Banking Platform organizes ~55 repositories into six domain prefixes. Each prefix defines the service's role, architectural tier, and framework starter.

| Prefix | Role | Tier | Framework Starter | Examples |
|--------|------|------|-------------------|----------|
| `core-banking-*` | Banking primitives: accounts, cards, ledger, payments | Core (CRUD + business rules) | `fireflyframework-starter-core` | `core-banking-accounts`, `core-banking-ledger`, `core-banking-payments`, `core-banking-cards`, `core-banking-psdx` |
| `core-lending-*` | Lending primitives: origination, servicing, credit, collateral | Core (CRUD + business rules) | `fireflyframework-starter-core` | `core-lending-loan-origination`, `core-lending-loan-servicing`, `core-lending-credit-scoring`, `core-lending-collateral-management`, `core-lending-mortgage` |
| `core-common-*` | Cross-cutting capabilities: customers, products, documents, security | Core (CRUD + business rules) | `fireflyframework-starter-core` | `core-common-customer-mgmt`, `core-common-product-mgmt`, `core-common-kycb-mgmt`, `core-common-user-mgmt`, `core-common-security-vault` |
| `core-data-*` | External data integration and templates | Core (data access) | `fireflyframework-starter-core` | `core-data-credit-bureaus`, `core-data-template` |
| `domain-*` | Orchestration of multiple core services into business workflows | Domain (orchestration) | `fireflyframework-starter-domain` | `domain-customer-people`, `domain-customer-kyc-kyb`, `domain-lending-loan-origination`, `domain-product-catalog`, `domain-distributor-branding` |
| `exp-*` | Backend-for-Frontend organized by user journey | Experience (BFF) | `fireflyframework-starter-application` | `exp-onboarding`, `exp-lending`, `exp-profile`, `exp-payments` |
| `app-*` | Entry-point applications: gateways, authenticators, configurators | Application (gateway/edge) | `fireflyframework-starter-application` | `app-authenticator`, `app-configurator-masters` |

Additional non-service repos:

| Repo | Purpose |
|------|---------|
| `firefly-parent` | Platform parent POM (extends `fireflyframework-parent`) |
| `firefly-bom` | Bill of Materials for all `com.firefly` artifacts |
| `firefly-config` | Centralized Spring Cloud Config repository (dev/pre/pro profiles) |

## 2. Package Naming

All Firefly platform code uses `groupId` = `com.firefly`. Java packages follow the pattern:

```
com.firefly.{tier}.{domain-short-name}.{module-layer}.{sub-package}
```

Real examples from the codebase:

| Repo | Module | Package Root |
|------|--------|-------------|
| `core-common-customer-mgmt` | `-interfaces` | `com.firefly.core.customer.interfaces.dtos` / `...interfaces.enums` |
| `core-common-customer-mgmt` | `-models` | `com.firefly.core.customer.models.entities` / `...models.repositories` |
| `core-common-customer-mgmt` | `-core` | `com.firefly.core.customer.core.services` / `...core.mappers` |
| `core-common-customer-mgmt` | `-web` | `com.firefly.core.customer.web.controllers` |
| `core-common-customer-mgmt` | `-sdk` | `com.firefly.core.customer.sdk.api` / `...sdk.model` / `...sdk.invoker` |
| `core-lending-loan-origination` | `-interfaces` | `com.firefly.core.lending.origination.interfaces.dtos` / `...interfaces.enums` |
| `core-lending-loan-origination` | `-core` | `com.firefly.core.lending.origination.core.services` / `...core.mappers` |
| `core-lending-loan-origination` | `-web` | `com.firefly.core.lending.origination.web` |
| `domain-customer-people` | `-core` | `com.firefly.domain.people.core.customer.commands` / `...core.customer.handlers` |
| `domain-customer-people` | `-infra` | `com.firefly.domain.people.infra` |
| `domain-customer-people` | `-web` | `com.firefly.domain.people.web.controller` |
| `domain-customer-people` | `-sdk` | `com.firefly.domain.people.sdk.api` / `...sdk.model` |
| `exp-onboarding` | `-web` | `com.firefly.exp.onboarding.web` / `...web.controllers` |
| `app-authenticator` | `-web` | `com.firefly.app.orchestrator.web` / `...web.controllers` |

Key pattern: the domain short name drops the prefix category and management suffix. For example, `core-common-customer-mgmt` becomes `core.customer`, and `domain-customer-people` becomes `domain.people`.

## 3. POM Inheritance Chain

Every service follows a three-level POM hierarchy:

```
org.fireflyframework:fireflyframework-parent (1.0.0-SNAPSHOT)
    |
    v
com.firefly:firefly-parent (1.0.0-SNAPSHOT)
    |
    v
com.firefly:{service-artifactId} (1.0.0-SNAPSHOT)
```

### What `fireflyframework-parent` provides

- Spring Boot parent (version management for Spring Boot, Spring Cloud, etc.)
- Plugin management (maven-compiler-plugin, spring-boot-maven-plugin, etc.)
- Dependency versions for framework artifacts (`org.fireflyframework` groupId)

### What `firefly-parent` adds on top

- **Imports two BOMs**: `fireflyframework-bom` (version `26.02.06`) and `firefly-bom` (version `1.0.0-SNAPSHOT`)
- **OpenAPI conventions**: Properties `base.package`, `openapi.base.package`, `openapi.model.package`, `openapi.api.package`
- **OpenAPI generation profile** (`generate-openapi`): always active unless `-DskipOpenApiGen`. Uses `spring-boot-maven-plugin` to start/stop a lightweight `OpenApiGenApplication`, then `springdoc-openapi-maven-plugin` to fetch the spec from `http://localhost:18080/v3/api-docs.yaml` and write it to `target/openapi/openapi.yml`
- **Skip-by-default control**: `openapi.gen.skip=true` by default; only `-web` modules set it to `false`

### Service-level POM pattern

```xml
<parent>
    <groupId>com.firefly</groupId>
    <artifactId>firefly-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <relativePath/>
</parent>

<artifactId>core-common-customer-mgmt</artifactId>
<packaging>pom</packaging>
<modules>
    <module>core-common-customer-mgmt-core</module>
    <module>core-common-customer-mgmt-interfaces</module>
    <module>core-common-customer-mgmt-models</module>
    <module>core-common-customer-mgmt-sdk</module>
    <module>core-common-customer-mgmt-web</module>
</modules>

<properties>
    <java.version>25</java.version>
</properties>
```

All services use Java 25 with `maven.compiler.release` set accordingly.

## 4. BOM Usage

`firefly-bom` (`com.firefly:firefly-bom:1.0.0-SNAPSHOT`) is a flat POM that manages versions for every artifact in the platform. It is imported automatically by `firefly-parent`, so individual services never need to import it directly.

**What it manages** (all at `1.0.0-SNAPSHOT`):

- **7 common libraries**: `library-common-core`, `library-common-web`, `library-common-r2dbc`, `library-common-auth`, `library-common-eda`, `library-common-cache`, `library-common-batch`
- **2 domain libraries**: `library-psps`, `library-rails`
- **3 utility libraries**: `library-utils`, `library-commons-validators`, `library-workflow-engine`
- **2 plugin manager libs**: `library-plugin-manager-core`, `library-plugin-manager-api`
- **5 core-banking** service artifacts + **5 matching SDKs**
- **11 core-lending** service artifacts + **11 matching SDKs**
- **15 core-common** service artifacts + **15 matching SDKs**
- **2 core-data** service artifacts + **2 matching SDKs**
- **10 domain** service artifacts + **10 matching SDKs**
- **3 application** artifacts + **1 app SDK** (`bfc-initconfig-sdk`)
- **1 flow SDK**: `firefly-flow-sdk`

**Version strategy**: All artifacts use `1.0.0-SNAPSHOT` during development. The BOM ensures no version conflicts across the platform.

When you add a dependency on another Firefly service's SDK, you omit the `<version>` tag because the BOM manages it:

```xml
<dependency>
    <groupId>com.firefly</groupId>
    <artifactId>core-common-customer-mgmt-sdk</artifactId>
    <!-- version managed by firefly-bom via firefly-parent -->
</dependency>
```

## 5. Module Structure

### Core services (`core-banking-*`, `core-lending-*`, `core-common-*`, `core-data-*`)

| Module | Content | Key Dependencies |
|--------|---------|-----------------|
| `{service}-interfaces` | DTOs, enums, API request/response contracts | `fireflyframework-utils`, `fireflyframework-validators`, `swagger-annotations`, `jackson-databind` |
| `{service}-models` | JPA/R2DBC entities, Spring Data repositories, Flyway migrations | `{service}-interfaces`, `fireflyframework-r2dbc`, `spring-boot-starter-data-r2dbc`, `r2dbc-postgresql`, `flyway-core` |
| `{service}-core` | Service interfaces, service impls, MapStruct mappers | `{service}-interfaces`, `{service}-models`, `fireflyframework-starter-core`, `mapstruct` |
| `{service}-web` | Spring Boot application class, REST controllers, `application.yaml` | `{service}-core`, `{service}-interfaces`, `fireflyframework-web`, `springdoc-openapi-starter-webflux-ui`, `micrometer-registry-prometheus` |
| `{service}-sdk` | Auto-generated API client from OpenAPI spec | `{service}-web` (provided scope), `openapi-generator-maven-plugin`, `okhttp3`, `swagger-annotations` |

### Domain services (`domain-*`)

| Module | Content | Key Dependencies |
|--------|---------|-----------------|
| `{service}-infra` | SDK client factories, `@ConfigurationProperties` for downstream base paths, third-party integration services (real or dummy) | Core service SDKs (e.g., `core-common-customer-mgmt-sdk`), `fireflyframework-utils` |
| `{service}-interfaces` | Domain-level DTOs and contracts | `{service}-core`, `fireflyframework-utils`, `fireflyframework-validators` |
| `{service}-core` | Commands, handlers, sagas/workflows, domain services (CQRS pattern) | `{service}-infra`, core SDKs, `fireflyframework-starter-domain` |
| `{service}-web` | REST controllers, Spring Boot app, `application.yaml` | `{service}-interfaces`, `fireflyframework-web`, `springdoc-openapi-starter-webflux-ui` |
| `{service}-sdk` | Auto-generated API client from OpenAPI spec | Same pattern as core SDKs |

### Experience services (`exp-*`)

| Module | Content | Key Dependencies |
|--------|---------|-----------------|
| `{service}-interfaces` | Journey-specific DTOs and API contracts | `fireflyframework-utils`, `fireflyframework-validators` |
| `{service}-infra` | SDK client factories for domain services | Domain service SDKs (e.g., `domain-customer-people-sdk`), `fireflyframework-utils` |
| `{service}-core` | Journey orchestration, response shaping, channel logic | `{service}-infra`, domain SDKs, `fireflyframework-starter-application` |
| `{service}-web` | REST controllers, Spring Boot app, `application.yaml` | `{service}-interfaces`, `fireflyframework-web`, `springdoc-openapi-starter-webflux-ui` |
| `{service}-sdk` | Auto-generated API client from OpenAPI spec | Same pattern as domain SDKs |

Note: Experience services consume **only** domain SDKs, never core SDKs directly. They have `-infra` for domain SDK client factories.

### Application services (`app-*`)

| Module | Content | Key Dependencies |
|--------|---------|-----------------|
| `{service}-interfaces` | Lightweight DTOs for gateway-level operations | `fireflyframework-utils` |
| `{service}-core` | Orchestration logic, security configuration | `fireflyframework-utils`, `mapstruct` |
| `{service}-web` | Gateway application, security config, controllers | `{service}-interfaces`, `fireflyframework-starter-application`, `spring-cloud-starter-gateway` |

Note: App services typically do **not** have `-models` or `-sdk` modules. They also do not have `-infra`.

## 6. Inter-Service Communication

### Synchronous: SDK Import

Services communicate synchronously by importing another service's `-sdk` module. The SDK is an auto-generated OpenAPI client using `openapi-generator-maven-plugin` with the `webclient` library (reactive).

**Concrete example** -- `domain-customer-people` calling `core-common-customer-mgmt`:

1. The `-infra` module declares the SDK dependency:
   ```xml
   <dependency>
       <groupId>com.firefly</groupId>
       <artifactId>core-common-customer-mgmt-sdk</artifactId>
   </dependency>
   ```

2. A `@ConfigurationProperties` class maps the base URL:
   ```java
   @ConfigurationProperties(prefix = "api-configuration.common-platform.customer-mgmt")
   public class CustomerMgmtProperties {
       private String basePath;
   }
   ```

3. A `ClientFactory` creates API beans:
   ```java
   @Component
   public class ClientFactory {
       private final ApiClient apiClient;

       public ClientFactory(CustomerMgmtProperties props) {
           this.apiClient = new ApiClient();
           this.apiClient.setBasePath(props.getBasePath());
       }

       @Bean
       public PartiesApi partiesApi() {
           return new PartiesApi(apiClient);
       }
       // ... more API beans
   }
   ```

4. `application.yaml` provides the base path:
   ```yaml
   api-configuration:
     common-platform.customer-mgmt:
       base-path: http://localhost:8082
   ```

5. Environment-specific overrides come from `firefly-config`:
   ```yaml
   # config/dev/application-dev.yaml
   endpoints:
     common:
       core-common-customer-mgmt: https://app-core-common-customer-mgmt.dev.soon.es
   ```

### Asynchronous: Events via Kafka

Domain services use the Firefly EDA (Event-Driven Architecture) library for async communication:

```yaml
firefly:
  eda:
    enabled: true
    default-publisher-type: KAFKA
    default-connection-id: default
    publishers:
      kafka:
        default:
          default-topic: domain-layer
          bootstrap-servers: localhost:9092
```

Domain services also enable CQRS and saga support:

```yaml
firefly:
  cqrs:
    enabled: true
    command:
      timeout: 30s
      metrics-enabled: true
      tracing-enabled: true
    query:
      timeout: 15s
      caching-enabled: true
      cache-ttl: 15m
  saga.performance.enabled: true
  stepevents.enabled: true
```

## 7. Layer Rules

```
+-------------------+
|  channel (web/    |  Browser, Mobile App, API Consumer
|  mobile/API)      |
+-------------------+
         |
         v
+-------------------+       +-------------------+
|  exp-* (BFF per   |       |  app-* (edge/     |
|  user journey)    |       |  gateway/auth)    |
+-------------------+       +-------------------+
         |                           |
         | calls via domain SDK      | calls via domain SDK or routes
         v                           v
+-------------------+
|   domain-* (orch) |  Workflows, Sagas, CQRS Commands/Queries
+-------------------+
         |
         | calls via SDK (sync) or Kafka events (async)
         v
+-------------------+
| core-* (CRUD/biz) |  Entities, Repositories, Business Rules
+-------------------+
         |
         | R2DBC / PostgreSQL
         v
+-------------------+
|    Database        |
+-------------------+
```

**Strict dependency direction -- violations break the architecture**:

| Rule | Rationale |
|------|-----------|
| Core NEVER imports a domain SDK | Core services are self-contained data owners |
| Core NEVER imports another core service SDK | Core services do not orchestrate each other |
| Domain orchestrates core services via their SDKs | Domain is the orchestration layer |
| Domain may import other domain SDKs only for cross-domain workflows | Keep coupling minimal |
| Experience (`exp-*`) consumes ONLY domain SDKs | Experience is a BFF layer, never accesses core directly |
| App orchestrates domain services (and occasionally core for infra concerns) | App is the edge entry point |
| Data services read from core but never write back | Data is a read projection |

**The `-infra` module exists ONLY in domain services**. It houses SDK client factories and configuration properties that wire up downstream core service connections. Core services do not have an `-infra` module because they do not call other services.

## 8. Configuration

### Spring Cloud Config Repository (`firefly-config`)

Centralized configuration lives in `firefly-config` with the following structure:

```
firefly-config/
  config/
    dev/
      application.yaml            # Local development defaults
      application-dev.yaml        # Dev environment endpoints for all services
      domain/
        customer-domain-people-dev.yaml
        customer-domain-kyc-kyb-dev.yaml
        lending-domain-loan-origination-dev.yaml
        ...
    pre/
      application-pre.yaml        # Pre-production endpoints
      domain/
        customer-domain-people-pre.yaml
        ...
    pro/
      application-pro.yaml        # Production endpoints
      domain/
        customer-domain-people-pro.yaml
        ...
```

### Environment Profiles

| Profile | Purpose | Endpoint pattern |
|---------|---------|-----------------|
| `dev` | Development | `https://app-{service-name}.dev.soon.es` |
| `pre` | Pre-production / staging | `https://app-{service-name}.pre.soon.es` |
| `pro` | Production | `https://app-{service-name}.pro.soon.es` |

### Service-Level Configuration (`application.yaml`)

Each service's `-web` module contains an `application.yaml` with:

- `spring.application.name` matching the repo name (e.g., `core-common-customer-mgmt`)
- Database connection via environment variables: `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_SSL_MODE`, `DB_USERNAME`, `DB_PASSWORD`
- R2DBC connection pool settings
- Flyway migrations from `classpath:db/migration`
- SpringDoc OpenAPI scanning the controller package
- Actuator endpoints: `health`, `info`, `prometheus`
- Liveness and readiness probes enabled
- Virtual threads enabled (`spring.threads.virtual.enabled: true`)

Domain services additionally configure `firefly.cqrs`, `firefly.eda`, `firefly.saga`, and `api-configuration` base paths for downstream SDKs.

### Profile-Specific Sections

Services embed multi-profile configuration using Spring's `---` document separator:

```yaml
---
spring:
  config:
    activate:
      on-profile: dev
logging:
  level:
    com.firefly: DEBUG

---
spring:
  config:
    activate:
      on-profile: prod
logging:
  level:
    com.firefly: INFO
springdoc:
  api-docs.enabled: false
  swagger-ui.enabled: false
```

## 9. API Contract Conventions

### OpenAPI Spec Ownership

Each service owns its OpenAPI spec. The spec is auto-generated at build time from the `-web` module's annotated controllers:

1. The `-web` module sets `openapi.gen.skip=false` and `openapi.gen.mainClass` to a lightweight Spring Boot application:
   ```xml
   <properties>
       <openapi.gen.skip>false</openapi.gen.skip>
       <openapi.gen.mainClass>com.firefly.core.customer.web.openapi.OpenApiGenApplication</openapi.gen.mainClass>
   </properties>
   ```

2. During `integration-test` phase, `spring-boot-maven-plugin` starts the app on port 18080, `springdoc-openapi-maven-plugin` fetches the spec from `/v3/api-docs.yaml`, and writes it to `target/openapi/openapi.yml`.

### SDK Generation

The `-sdk` module uses `openapi-generator-maven-plugin` (v7.0.1) to generate a Java client from the spec:

```xml
<configuration>
    <inputSpec>${project.parent.basedir}/${project.parent.artifactId}-web/target/openapi/openapi.yml</inputSpec>
    <generatorName>java</generatorName>
    <library>webclient</library>
    <apiPackage>com.firefly.core.customer.sdk.api</apiPackage>
    <modelPackage>com.firefly.core.customer.sdk.model</modelPackage>
    <invokerPackage>com.firefly.core.customer.sdk.invoker</invokerPackage>
    <configOptions>
        <reactive>true</reactive>
        <returnResponse>true</returnResponse>
        <useTags>true</useTags>
        <dateLibrary>java8-localdatetime</dateLibrary>
    </configOptions>
</configuration>
```

### How Consumers Import SDKs

Consumers add the SDK as a Maven dependency (version managed by the BOM) and create API beans through a `ClientFactory` in their `-infra` module. See section 6 for the full pattern.

### REST API Conventions

Controllers observed in the codebase follow these patterns:

- Base path: `/api/v1/{resource}`
- Fully reactive: all endpoints return `Mono<ResponseEntity<T>>`
- `@Tag` annotation on every controller for OpenAPI grouping
- `@Operation` annotation on every endpoint with `summary` and `description`
- `@Valid` on request bodies
- UUID-based resource identifiers
- Sub-resources under parent: `/api/v1/customers/{partyId}/addresses/{addressId}`
- Swagger UI at `/swagger-ui.html`, API docs at `/v3/api-docs`
- Packages scanned via `springdoc.packages-to-scan`
- Paths filtered via `springdoc.paths-to-match: /api/**`

## 10. Quick Reference

## 11. SDK Model DTO Patterns

Generated SDK model DTOs (from `openapi-generator-maven-plugin`) have patterns that differ from hand-written DTOs:

### Read-Only ID Fields

ID fields (`dateCreated`, `dateUpdated`, and the entity ID) are **read-only** -- set only via the constructor:

```java
// CORRECT -- set ID via constructor
KycVerificationDTO dto = new KycVerificationDTO(null, null, UUID.randomUUID());

// WRONG -- setter does not exist for ID fields
KycVerificationDTO dto = new KycVerificationDTO();
dto.setKycVerificationId(UUID.randomUUID());  // Compilation error
```

### Fluent Setters for Mutable Fields

Mutable fields use fluent setters (return `this`):

```java
SendNotificationCommand command = new SendNotificationCommand()
    .partyId(partyId)
    .notificationType(SendNotificationCommand.NotificationTypeEnum.WELCOME)
    .channels(List.of(SendNotificationCommand.ChannelsEnum.EMAIL));
```

### Inline Enums

Enum fields generate **inner enum classes** within the DTO:

```java
// NOT a standalone enum -- it is an inner class of the DTO
SendNotificationCommand.NotificationTypeEnum.WELCOME
SendNotificationCommand.PriorityEnum.NORMAL
```

### Getter Naming

Getters use the **full field name** from the OpenAPI spec:
- `getKycVerificationId()` (NOT `getId()`)
- `getVerificationDocumentId()` (NOT `getDocumentId()`)
- `getComplianceCaseId()` (NOT `getCaseId()`)

## 12. Module Dependency Rules

### -web MUST depend on -core

When controllers reference service interfaces, commands, or any types from `-core`, the `-web` module must declare the dependency:

```xml
<!-- {service}-web/pom.xml -->
<dependency>
    <groupId>com.firefly</groupId>
    <artifactId>{service}-core</artifactId>
</dependency>
```

### -interfaces MUST NOT depend on -core

This creates an inverted/circular dependency. Correct direction:

```
-core → -interfaces (core uses DTOs from interfaces)
-web  → -core       (web uses services from core)
-web  → -interfaces (web uses DTOs from interfaces)
```

### NotImplementedException Requires fireflyframework-web

If a `-core` module needs `NotImplementedException` (from `org.fireflyframework.web.error.exceptions`), it must add `fireflyframework-web` as a dependency. The starters `fireflyframework-starter-core` and `fireflyframework-starter-domain` do NOT include it transitively.

### StepStatus Enum Values

The `StepStatus` enum has values: `PENDING`, `RUNNING`, `DONE`, `FAILED`, `SKIPPED`, `TIMED_OUT`, `RETRYING`. `StepStatus.COMPLETED` does **not** exist -- use `StepStatus.DONE`.

## 13. ClientFactory Conventions

In domain services, the `-infra` module wires SDK clients using `ClientFactory` beans:

```java
// CORRECT -- @Component, not @Configuration
@Component
public class KycbClientFactory {
    private final ApiClient apiClient;

    public KycbClientFactory(KycbMgmtProperties props) {
        this.apiClient = new ApiClient();
        this.apiClient.setBasePath(props.getBasePath());
    }

    @Bean
    public KycVerificationApi kycVerificationApi() {
        return new KycVerificationApi(apiClient);
    }
}
```

**Key conventions:**
- Use `@Component` (not `@Configuration`) -- this is a banking platform convention
- Use `@ConfigurationProperties` without `@Configuration` on the properties class (when `@ConfigurationPropertiesScan` is on the application class)
- One `ClientFactory` per downstream service
- One `@Bean` method per API class from the SDK

## 14. Cross-Layer Reflexive Property

When generating code for an upper-layer service (domain/app) that calls a lower-layer service (core), if the lower-layer method does not exist, you MUST create it:

1. Create the endpoint in the core service's `-web` controller
2. Create the service interface and implementation in `-core`
3. Rebuild the core service's SDK
4. Then implement the upper-layer call

**Never** leave upper-layer methods returning static/mock data or empty results. If the core service is not yet available, define a port interface with a stub adapter that throws `NotImplementedException`.

## 15. Javadoc and README Documentation Standards

### Javadoc

All public classes and methods must include Javadoc:

```java
/**
 * Orchestrates KYC verification lifecycle including identity checks,
 * document collection, and compliance case management.
 */
@Service
public class KycServiceImpl implements KycService {

    /**
     * Initiates identity verification for the given compliance case.
     *
     * @param caseId the compliance case identifier
     * @return a {@link SagaResult} with verification outcome
     */
    public Mono<SagaResult> verify(UUID caseId) { ... }
}
```

### README.md

Every microservice MUST have a `README.md` at the project root with:

1. **Service description** -- purpose and role in the platform
2. **Architecture** -- tier, dependencies, how it fits in the broader platform
3. **Module structure** -- table of modules and their purposes
4. **API reference** -- main endpoints with HTTP method, path, and description
5. **Configuration** -- required environment variables and properties
6. **Getting started** -- build and run instructions
7. **Dependencies** -- upstream (calls) and downstream (called by) services

## 16. PII Logging Rules

Never log personally identifiable information. Use resource identifiers instead:

```java
// WRONG
log.info("Processing customer: name={}, email={}", name, email);

// CORRECT
log.info("Processing customer: partyId={}", partyId);
```

## 17. Quick Reference

| Do | Don't |
|----|-------|
| Extend `firefly-parent` as your service's parent POM | Extend `fireflyframework-parent` directly -- you lose BOM imports and OpenAPI profile |
| Use the five-module structure for core services: `-interfaces`, `-models`, `-core`, `-web`, `-sdk` | Put entities in `-core` or controllers in `-interfaces` |
| Use the domain module structure with `-infra` for SDK client factories | Put `ClientFactory` or `@ConfigurationProperties` in `-core` |
| Omit `<version>` for any `com.firefly` dependency -- the BOM manages it | Hard-code versions for platform artifacts |
| Name packages `com.firefly.{tier}.{domain-short}.{layer}` | Use `com.firefly.{full-repo-name}` -- it creates unnecessarily long packages |
| Place DTOs and enums in `-interfaces`, entities in `-models` | Mix DTOs and entities in the same module |
| Generate SDKs from OpenAPI specs using the `-web` module's controllers | Write SDK clients by hand |
| Configure downstream service base paths via `api-configuration.*` in `application.yaml` | Hard-code URLs in Java classes |
| Use `fireflyframework-starter-core` for core services, `fireflyframework-starter-domain` for domain services, `fireflyframework-starter-application` for experience (`exp-*`) and app services | Mix starters across tiers |
| Use CQRS commands/handlers/sagas in domain `-core` modules | Use plain CRUD services in domain modules -- that belongs in core |
| Set `openapi.gen.skip=false` only in the `-web` module | Enable OpenAPI generation in non-web modules |
| Use R2DBC with PostgreSQL for data access in `-models` | Use blocking JDBC -- the platform is fully reactive (WebFlux) |
| Communicate async between domain services via Kafka (Firefly EDA) | Call domain SDKs synchronously for fire-and-forget operations |
| Use environment variables for database credentials (`DB_HOST`, etc.) | Commit credentials in `application.yaml` |
| Place Flyway migrations in `-models/src/main/resources/db/migration` | Place migrations anywhere else |
| Set `spring.application.name` to the repo name (e.g., `core-common-customer-mgmt`) | Use a different naming convention for the Spring app name |
| Use `spring.threads.virtual.enabled: true` | Use thread pools for request handling |
| Expose actuator endpoints: `health`, `info`, `prometheus` | Expose all actuator endpoints in production |
| Disable Swagger UI in `prod` profile | Leave API docs publicly accessible in production |
| Use `@Component` on `ClientFactory` classes | Use `@Configuration` on `ClientFactory` classes |
| Use `@ConfigurationProperties` alone (with `@ConfigurationPropertiesScan`) | Combine `@Configuration` with `@ConfigurationProperties` |
| Use `@Valid @RequestBody` on controller POST/PUT bodies | Omit `@Valid` on `@RequestBody` |
| Use `scanBasePackages = "org.fireflyframework.web"` | Use `scanBasePackages = "com.firefly.common.web"` |
| Use `springdoc.packages-to-scan: *.web.controllers` (plural) | Use `*.web.controller` (singular) |
| Use `health.show-details: when-authorized` | Use `health.show-details: always` |
| Use profile names `dev`, `pre`, `pro` | Use `testing`, `staging`, `local` |
| Use `StepStatus.DONE` | Use `StepStatus.COMPLETED` (does not exist) |
| Set SDK DTO IDs via constructor: `new DTO(null, null, id)` | Use `dto.setId()` -- SDK IDs are read-only |
| Implement full call chain to lower-layer services | Return static/mock data from service methods |
| Log only resource IDs (`partyId`, `paymentId`) | Log PII (names, emails, IBANs, phone numbers) |
| Include Javadoc on all public classes and methods | Skip documentation entirely |
| Include `README.md` in every microservice root | Leave services undocumented |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.