The layers around your business rules¶
Spring Boot and WebFlux host the application and handle HTTP, dependency injection, and reactive execution. Jafly adds command and query contracts, coordination engines, and integration bridges. Your service defines the business operation, including permission checks and what happens when work fails or repeats.
Divide the responsibilities¶
| Layer | Responsibility | Examples |
|---|---|---|
| Native Spring platform | Host the service and supply its infrastructure APIs | WebFlux routing, Spring dependency injection, Reactor, Spring Data, Spring Security |
| Jafly modules | Give operations a shared shape and connect configured components | Commands/queries, policy ports, saga/TCC engines, event bridges, adapters, observability |
| Your service | Define what the business operation means and how it behaves under failure | Handlers, permissions, trusted tenant context, transaction scope, idempotency, compensation logic |
If your team already has conventions for an application layer, you may not need the corresponding Jafly module. Choose a contract or adapter when it solves a coordination or integration problem in your service.
Follow an onboarding application¶
In the first service, POST /applications sends SubmitApplication to a handler that saves and returns OnboardingApplication. The same path can support a larger business operation, with the service defining any persistence and failure policy. The tutorial does not enable the optional bridges shown below.
- Your controller accepts input. Spring WebFlux handles HTTP routing and request validation. The controller constructs a typed command and sends it through
CommandBus. - The bus prepares execution. It finds the registered handler, establishes available correlation context, validates the command, and runs command authorization when configured. Annotation validation requires a Jakarta
Validatorbean. - Your handler makes the decision. A concrete
CommandHandler<C, R>executes the business operation and returnsMono<R>. Data writes and external calls remain explicit in that operation. - The result completes the call. A handler annotated for event publishing can pass its result to the configured event bridge before returning. Event publishing and query-cache behavior depend on actual annotations and beans.
The first service tutorial builds this path and tests its HTTP response.
Conditional bridges¶
The CQRS/EDA configuration creates the command-event bridge when an EventPublisherFactory bean exists. A @PublishDomainEvent handler enables the result-to-event path. Transport selection is owned by the EDA publisher factory and its configured transport.
The query-cache invalidation bridge requires the relevant cache beans and matching @InvalidateCacheOn event types. The bridge listens to Spring application events. Broker events reach that path only when the service connects them.
A command can still return successfully when event publication fails. The bus and bridge do not turn independent work into a distributed transaction or exactly-once processing. Define the application's transaction, retry, and idempotency policy from the chosen storage and transport behavior.
Sources: command bus pipeline, bridge conditions, event publisher factory.
Find a capability¶
These illustrations group capabilities. Their positions carry no meaning about runtime dependencies, deployment, or delivery guarantees.
| Capability | Start with | Design decision |
|---|---|---|
| Security and identity | Security API, resource server, policy adapters | Which principal and policy source may authorize an operation? |
| Business operations | CQRS, business rules | Where do validation, authorization, and business decisions belong? |
| External services | Client, notifications, callbacks, agentic bridge | Which provider contract and retry behavior fit this call? |
| Event-driven integration | EDA, webhooks | Which transport, envelope, and delivery behavior does the service need? |
| Domain and data | R2DBC, event sourcing, cache, ECM | What is persisted, cached, reconstructed, or retained? |
| Business coordination | Orchestration | Which steps, compensations, and recovery checks define the operation? |
| Service operations | Observability, web, configuration | Which metrics, traces, HTTP conventions, and configuration does the service operate? |
| Team-wide architecture | Parent/BOM, starters, kernel, plugins | Which shared contracts and dependency conventions should teams adopt? |
Check each provider's behavior¶
Security and messaging expose shared contracts with separate provider adapters. For authorization, the application asks a policy port for a decision and the configured adapter supplies the answer. For events, the common publishing API still leaves each transport with its own failure and delivery behavior.
For example, the PostgreSQL EDA publisher uses its own connection outside a business transaction. The event-sourcing module can write its configured outbox in the event-store transaction; that is a different implemented boundary. Read the selected module's contract before relying on atomicity.
Check outbound clients in the same way. SOAP and gRPC do not necessarily use the REST client's retry and circuit-breaker policies. Select a request policy that fits whether the operation can safely repeat.
Align the libraries, then build the service¶
Use the framework parent or independent BOM to align library versions, and a starter when its dependency set fits your service layer. Test your provider configuration and define business policy in the application.
Choose the next dependency in the module catalog, or read the product introduction at jafly on Framework by Firefly.