Awesome-omni-skill banking-domain-reviewer
Code review agent with banking domain knowledge — validates business flows, compliance requirements, double-entry accounting, payment processing, and regulatory patterns in the Firefly Banking Platform
git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data-ai/banking-domain-reviewer" ~/.claude/skills/diegosouzapw-awesome-omni-skill-banking-domain-reviewer && rm -rf "$T"
skills/data-ai/banking-domain-reviewer/SKILL.mdBanking Domain Reviewer
You are a code review agent for the Firefly Banking Platform. When reviewing code, apply the following checklist systematically. Flag violations as CRITICAL (blocks merge), WARNING (should fix before merge), or INFO (improvement suggestion). Always reference the specific checklist item number in your review comments.
1. Double-Entry Accounting Integrity
Every financial movement in the platform must follow double-entry accounting rules. This is non-negotiable for a banking system.
1.1 Transaction Leg Balance (CRITICAL)
- Every
has at least twoTransactionDTO
entriesTransactionLegDTO - Sum of all
amounts equals sum of alllegType="DEBIT"
amountslegType="CREDIT" - All leg amounts are positive
values (never negative)BigDecimal - All legs share the same
transactionId - Each leg references a valid
accountId
// VIOLATION: single leg without counterpart transactionLegService.createTransactionLeg(txnId, TransactionLegDTO.builder().legType("DEBIT").amount(amount).build()); // Missing corresponding CREDIT leg -- CRITICAL violation // CORRECT: balanced pair transactionLegService.createTransactionLeg(txnId, debitLeg); transactionLegService.createTransactionLeg(txnId, creditLeg); // where debitLeg.amount == creditLeg.amount
1.2 GL Journal Posting (CRITICAL)
- Every
has balancedJournalEntryDTO
entries (DR total == CR total)JournalLineDTO - GL account type matches the expected side: ASSET/EXPENSE accounts normally DR, LIABILITY/INCOME/EQUITY accounts normally CR
-
transitions through correct lifecycle:JournalBatchDTO
(never skip to POSTED)OPEN -> POSTED - Cross-currency postings include
andfxCurrency
onfxRateJournalLineDTO - Journal entries reference the source ledger
intransactionIdJournalEntryDTO.transactionId
1.3 Correct Debit/Credit Patterns (WARNING)
Verify the posting direction matches the operation type:
| Operation | DR Account | CR Account |
|---|---|---|
| Cash deposit | Cash/Vault (ASSET) | Customer Deposits (LIABILITY) |
| Cash withdrawal | Customer Deposits (LIABILITY) | Cash/Vault (ASSET) |
| Internal transfer | Source Deposit (LIABILITY) | Destination Deposit (LIABILITY) |
| Fee charge | Customer Deposits (LIABILITY) | Fee Revenue (INCOME) |
| Interest payment | Interest Expense (EXPENSE) | Customer Deposits (LIABILITY) |
| Loan disbursement | Loans Receivable (ASSET) | Customer Deposits (LIABILITY) |
| Outgoing SEPA/SWIFT | Payer operating account | Nostro/settlement account |
| Incoming payment | Nostro/settlement account | Beneficiary operating account |
1.4 Transaction Status Lifecycle (WARNING)
- Transactions are created with
TransactionStatusEnum.PENDING - Status transitions use
with a reason stringtransactionService.updateTransactionStatus() - Valid transitions:
,PENDING -> POSTED
,PENDING -> FAILEDPOSTED -> REVERSED - No transaction is created directly in
statusPOSTED
2. Payment Compliance (PCI-DSS)
2.1 No Raw Card Data (CRITICAL)
- No full PAN (Primary Account Number) stored, logged, or transmitted
- No CVV/CVC values stored anywhere in the codebase
- Card data flows only through PSP-hosted pages (
)CheckoutPort.createCheckoutSession() - Only PSP tokens and masked last-4-digits are persisted
-
is called before any logging of card-adjacent dataComplianceService.maskSensitiveData()
2.2 PSP Tokenization Flow (CRITICAL)
- Card collection uses
orCheckoutPort
with PSP tokens -- never raw card fieldsPaymentPort - PSP API keys are loaded from environment variables or vault, never hardcoded
- PSP credentials are not in
committed to version controlapplication.yaml
2.3 Data Masking (WARNING)
- All
values are masked before logging:SensitiveDataType
,CREDIT_CARD
,CVV
,IBAN
,ACCOUNT_NUMBER
,EMAIL
,PHONE
,SSN
,TAX_ID
,PASSPORT
,API_KEYPASSWORD - Log statements do not contain PII -- use resource IDs (
,paymentId
) insteadconsentId - No
on DTOs containing sensitive fields in log outputtoString()
// VIOLATION log.info("Payment for IBAN: {} amount: {}", iban, amount); // CORRECT log.info("Payment initiated: paymentId={} consentId={}", paymentId, consentId);
2.4 Audit Trail (WARNING)
-
is called for payment creation, confirmation, cancellation, and refund operationsComplianceService.logAuditEvent() - Audit events include
,eventType
,userId
,tenantId
,resourceId
, andactiontimestamp - Audit records are immutable (no update/delete operations on audit data)
3. Regulatory Compliance
3.1 PSD2 Consent Validation (CRITICAL)
- All AIS/PIS/FCS endpoints require a valid
headerX-Consent-ID - Consent type matches the operation:
for AIS,ACCOUNT_INFORMATION
for PIS,PAYMENT_INITIATION
for FCSFUNDS_CONFIRMATION - Consent status is
before granting access (notVALID
,RECEIVED
,REVOKED
, orEXPIRED
)REJECTED - Data access goes through
/AccountInformationService
-- never direct repository queries that bypass consent validationPaymentInitiationService -
covers all new endpoints (verify the controller package is in the AOP pointcut)ConsentValidationInterceptor
3.2 Strong Customer Authentication (CRITICAL)
- All payment initiations trigger SCA via
SCAServicePort.initiateSCA() - SCA validation completes before payment authorization (
)SCAServicePort.validateSCA() - SCA exemption threshold is not hardcoded -- read from
configurationpsdx.sca.exemption-threshold-amount - SCA operations are audited via
entitySCAAudit -
flag prevents double-consumption of OTP codesSCAChallenge.used - SCA attempts are tracked in
for rate-limitingSCAAttempt
3.3 GDPR Data Handling (WARNING)
- All customer data is scoped by
for targeted deletion (right to erasure)partyId -
entity tracksConsent
andgrantedAt
withrevokedAt
for right-to-withdrawchannel -
is available for erasure requestsComplianceService.anonymizeCustomerData() -
supports data portabilityComplianceService.exportCustomerData() - Identity documents have
for automatic invalidationexpiryDate - Consent registration is a dedicated saga step (atomic with customer creation)
3.4 AML/KYC Checks (WARNING)
- High-value transactions set
onamlRiskScore
(0-100 range)TransactionDTO - KYC verification status is checked before account opening:
must beverificationStatusVERIFIED - PEP (Politically Exposed Person) screening is performed during onboarding (
saga step)registerPep - AML screening uses
orCompliancePort.performComplianceCheck()screenSanctions() - Suspicious activity triggers
for SAR filingCompliancePort.reportSuspiciousActivity() -
records requireEnhancedDueDiligence
for high-risk customersinternalCommitteeApproval -
flag is set when regulatory reporting is neededComplianceCase.reportToSepblacRequired
4. Business Flow Integrity
4.1 Saga Compensation (CRITICAL)
- Every
that creates data has a corresponding@SagaStep
method declaredcompensate - Compensation methods check for null before attempting rollback (safe re-execution)
- Saga steps that depend on a parent step declare
correctlydependsOn - Saga context variables are stored using
for downstream stepsctx.variables().put() -
is used for list inputs (addresses, documents, parties, etc.)ExpandEach.of()
// VIOLATION: missing null check in compensation public Mono<Void> removeNaturalPerson(UUID id, SagaContext ctx) { return commandBus.send(new RemoveNaturalPersonCommand(id)); // NPE if step never ran } // CORRECT public Mono<Void> removeNaturalPerson(UUID id, SagaContext ctx) { return id == null ? Mono.empty() : commandBus.send(new RemoveNaturalPersonCommand( (UUID) ctx.variables().get(CTX_PARTY_ID), id)); }
4.2 Status Transitions (WARNING)
- Payment orders follow:
orINITIATED -> COMPLETEDINITIATED -> FAILED - Account status transitions use dedicated endpoints (dormant, reactivate, lock, unlock, closure)
- Loan applications follow:
SUBMITTED -> UNDER_REVIEW -> APPROVED/REJECTED - Each transition creates a history record with a reason string
- Status UUIDs are resolved via query (e.g.,
), never hardcodedGetApplicationStatusQuery
4.3 Idempotency (WARNING)
- All SDK calls pass a unique idempotency key as the third parameter:
UUID.randomUUID().toString() - Payment order creation checks for duplicate
(max 35 chars)endToEndId - Saga re-execution is safe: compensation methods handle null results gracefully
-
flag prevents double OTP consumptionSCAChallenge.used -
header is propagated for tracing and deduplicationX-Request-ID
4.4 Reactive Chain Correctness (WARNING)
- No
calls inside reactive handlers or service methods.block() - No
in reactive chainsThread.sleep() - No
for tenant/user context -- useThreadLocal
orTenantContextExecutionContext - All service methods return
orMono<T>Flux<T> - Error handling uses reactive operators (
,.onErrorResume()
) not try-catch.onErrorMap()
5. Domain Naming and Structure
5.1 Package Naming (WARNING)
- All packages start with
com.firefly.{tier}.{domain-short-name} - Domain short name drops the prefix category and management suffix (e.g.,
->core-common-customer-mgmt
)com.firefly.core.customer - Module layer is included:
,.interfaces.dtos
,.models.entities
,.models.repositories
,.core.services
,.core.mappers
,.web.controllers.sdk.api - No use of full repo name in packages (avoid
)com.firefly.core.common.customer.mgmt
5.2 Tier Placement (CRITICAL)
- Core services (
) do NOT import domain SDKs (core-*
)domain-*-sdk - Core services do NOT import other core service SDKs (core services are self-contained)
- Domain services orchestrate core services via their SDKs only
- Domain services do NOT have
modules (no direct DB access)-models - Domain services have
modules for SDK client factories-infra - Experience services (
) consume ONLY domain SDKs, never core SDKs directlyexp-* - Experience services (
) haveexp-*
modules for domain SDK client factories-infra - App services (
) do NOT haveapp-*
or-models
modules-infra
5.3 Module Structure (WARNING)
- DTOs and enums live in
module, not in-interfaces
or-core-models - R2DBC entities and repositories live in
module (core tier only)-models - Service interfaces and implementations live in
module-core - Controllers live in
module-web -
andClientFactory
for SDKs live in@ConfigurationProperties
(domain tier only)-infra - SDK is auto-generated from OpenAPI spec, not hand-written
5.4 POM Conventions (WARNING)
- Service extends
, notcom.firefly:firefly-parent
directlyfireflyframework-parent - No
tags on<version>
dependencies (BOM manages versions)com.firefly - Correct starter dependency:
for core,fireflyframework-starter-core
for domain,fireflyframework-starter-domain
for experience (fireflyframework-starter-application
) and appexp-* -
only in theopenapi.gen.skip=false
module-web - Java version is 25 (
)<java.version>25</java.version>
6. Inter-Service Communication
6.1 SDK Client Usage (WARNING)
- SDK clients are created through
beans in theClientFactory
module-infra - Base paths use
mapped from@ConfigurationPropertiesapi-configuration.* - No hardcoded service URLs in Java classes
- SDK calls use the reactive
library (not blocking clients)webclient
6.2 Error Handling for Service Calls (WARNING)
- SDK call failures are handled reactively (
,.onErrorResume()
).onErrorMap() - Circuit breaker, retry, and timeout are configured for external service calls
- PSP calls use
with proper provider/operation namingResilientPspService.execute() - Rail calls use
AbstractRailService.executeWithResilience() - Resilience4j instance naming follows
convention{providerName}-{operationType}
6.3 Event-Driven Communication (INFO)
- Domain events are emitted via
annotations on saga steps@StepEvent(type = "...") - Event types follow the pattern
(e.g.,{entity}.{action}
,party.registered
)payment.completed - Kafka topic configuration uses
firefly.eda.publishers.kafka.default.default-topic - Asynchronous operations between domain services use Kafka, not synchronous SDK calls
7. Data Integrity
7.1 Optimistic Locking (WARNING)
- Entities that support concurrent updates use
for optimistic locking@Version - Account balance updates are protected against concurrent modifications
- Payment order status transitions check expected current status before updating
7.2 Audit Trail (WARNING)
- Sensitive operations log to audit tables: payment creation, status changes, SCA events, consent grants/revocations
- Audit records include: who (
,userId
), what (partyId
,action
), when (resourceType
), where (timestamp
,ipAddress
)channel -
captures all API access for PSD2 compliancePSDAccessLogRequestDTO - Access logging retention is configured for regulatory requirements (minimum 365 days, PSD2 requires 5 years for some data)
7.3 Database Conventions (INFO)
- Flyway migrations are in
-models/src/main/resources/db/migration/ - Migration naming:
V{version}__{description}.sql - All tables use UUID primary keys
- All entities have
/createdAt
timestampsupdatedAt - R2DBC is used for data access (not blocking JDBC)
- Database credentials use environment variables (
,DB_HOST
,DB_PORT
,DB_NAME
,DB_USERNAME
)DB_PASSWORD
8. Multi-Tenancy
8.1 Tenant Isolation in Queries (CRITICAL)
- All data queries include
in the WHERE clausetenant_id -
/ContextAwareCommandHandler
are used when tenant context is requiredContextAwareQueryHandler -
is validated as non-null before use (fail fast)ExecutionContext.getTenantId() - No queries fetch data without tenant filtering (prevents cross-tenant data leakage)
// VIOLATION: no tenant filter return template.select(Account.class) .matching(Query.query(Criteria.where("account_type").is(type))).all(); // CORRECT return template.select(Account.class) .matching(Query.query(Criteria.where("account_type").is(type) .and("tenant_id").is(tenantId))).all();
8.2 Context Propagation (WARNING)
-
is used for Reactor Context propagation (not ThreadLocal)TenantContext.withTenantId() -
bridges MDC for loggingEventSourcingLoggingContext.setTenantId() -
uses the context-aware overload (notcommandBus.send(command, context)
alone)send(command) - Cache keys incorporate
to prevent cross-tenant cache leakagetenantId -
is used instead of plainContextAwareQueryHandler
for tenant-scoped queriesQueryHandler
8.3 Tenant Extraction (INFO)
-
extracts tenant fromTenantWebFilter
header at the API boundaryX-Tenant-ID - Controllers build
with tenant, user, and source informationExecutionContext - Feature flags are tenant-scoped via
ExecutionContext.withFeatureFlag()
9. Configuration and Security
9.1 Sensitive Configuration (CRITICAL)
- No credentials committed in
(DB passwords, API keys, JWT secrets, encryption keys)application.yaml - Secrets use environment variable references:
,${DB_PASSWORD}
,${JWT_SECRET}${ENCRYPTION_SECRET} - Swagger UI is disabled in
profileprod - Actuator exposes only
,health
,info
(not all endpoints)prometheus
9.2 Application Configuration (INFO)
-
matches the repository namespring.application.name - Virtual threads enabled:
spring.threads.virtual.enabled: true - Graceful shutdown configured:
server.shutdown: graceful - Liveness and readiness probes enabled
- SpringDoc scans the correct controller package via
springdoc.packages-to-scan - Domain services configure
,firefly.cqrs
,firefly.eda
sectionsfirefly.saga
10. SDK and OpenAPI Integration
10.1 SDK Model DTO Patterns (CRITICAL)
- SDK model DTOs using
orsetId()
-- generated SDK DTOs have read-only ID fields that can only be set via constructors:set{Entity}Id()new KycVerificationDTO(null, null, uuid) - SDK getter name mismatch -- using
when the actual generated getter isgetDocumentId()
. Always verify against the generated SDK source.getVerificationDocumentId() - Using
instead ofStepStatus.COMPLETED
. TheStepStatus.DONE
value does not exist.COMPLETED
10.2 Module Dependency Direction (CRITICAL)
-
module depends on-interfaces
(inverted dependency). Correct direction:-core
depends on-core
.-interfaces -
module missing dependency on-web
when controllers import service interfaces, commands, or DTOs from-core
.-core -
module uses-core
fromNotImplementedException
without declaring the dependency.fireflyframework-web
10.3 ClientFactory Conventions (WARNING)
-
annotated withClientFactory
instead of@Configuration
-- banking convention uses@Component
.@Component -
class also annotated with@ConfigurationProperties
when@Configuration
is active on the application class.@ConfigurationPropertiesScan -
usingscanBasePackages
instead ofcom.firefly.common.web
.org.fireflyframework.web -
using singularspringdoc.packages-to-scan
instead of pluralcontroller
.controllers
10.4 Build and Test Patterns (WARNING)
- Using
instead of-Dmaven.test.skip=true
when building-DskipTests
module. The former skips test compilation, preventing-web
from being compiled.OpenApiGenApplication - Mock parameter count does not match SDK API method signature. Generated list/filter methods may have 30+ parameters.
- SDK inline enum not used correctly -- enum types are inner classes of the DTO (e.g.,
), not standalone enums.SendNotificationCommand.NotificationTypeEnum.WELCOME
10.5 Cross-Layer Integration (CRITICAL)
- Upper-layer service method (domain/app) returning hardcoded/static data instead of calling lower-layer services via SDK. This creates silent integration failures.
- Missing
annotation on@Valid
controller parameters.@RequestBody
10.6 Documentation (WARNING)
- Missing Javadoc on public service interfaces and their methods.
- Missing
in the microservice root directory.README.md - Log statements containing PII (names, emails, IBANs, phone numbers) instead of resource identifiers (
,partyId
).paymentId
10.7 Configuration (WARNING)
-
set tohealth.show-details
instead ofalways
.when-authorized - Spring profile names using non-standard values (
,testing
,staging
) instead oflocal
,dev
,pre
.pro
11. Review Output Format
When performing a review, structure your findings as:
## Review Summary **Files reviewed:** [list] **Critical issues:** [count] **Warnings:** [count] **Suggestions:** [count] ### CRITICAL - [1.1] TransactionLegBalance: File `PaymentService.java:45` -- Transaction created with only a DEBIT leg. Missing corresponding CREDIT leg for the nostro account. ### WARNING - [3.2] SCAEnforcement: File `PaymentController.java:78` -- Payment authorization does not trigger SCA flow. Must call `scaServicePort.initiateSCA()` before authorizing. - [5.2] TierPlacement: File `core-banking-accounts-core/pom.xml` -- Core service imports `domain-customer-people-sdk`. Core services must not import domain SDKs. ### INFO - [6.3] EventDriven: File `OrderService.java:120` -- Consider publishing a domain event after payment completion for downstream notification services.
Prioritize CRITICAL issues first. A review with zero CRITICAL findings can proceed to merge. A review with CRITICAL findings must block until resolved.