Starters are opinionated bundles that activate every framework
module a given service tier needs in a single decorator. They mirror
org.fireflyframework.starter.* (Java) and
FireflyFramework.Starter.* (.NET) so a service that's been written
on one platform reads the same way on every other.
| Starter | Activates | Use it for |
|---|---|---|
Core (enable_core_stack) |
web, server, observability, metrics, tracing, cache, EDA, CQRS, resilience, actuator (+ metrics), AOP | Any infra-tier service. The foundation every other starter pulls in. |
Web (enable_web_stack) |
web, server, observability, metrics, tracing, actuator (+ metrics), resilience | Pure HTTP/REST APIs that don't need EDA, CQRS or cache. |
Application (enable_application_stack) |
core stack + plugins, security (JWT + password), sessions, i18n, scheduling, transactional engine, IDP, callbacks, webhooks, notifications | Application/orchestration tier with auth, scheduling and integrations. |
Data (enable_data_stack) |
core stack + relational, document, HTTP client, scheduling, resilience | Data ingestion / enrichment / batch services. |
Domain (enable_domain_stack) |
core stack + event sourcing, transactional engine, rule engine, relational, HTTP client, plugins. Re-exports every pyfly.domain DDD primitive. |
DDD-style domain microservices. |
from pyfly.core import pyfly_application
from pyfly.starters.domain import enable_domain_stack
@enable_domain_stack
@pyfly_application(name="my-service", scan_packages=["my_service"])
class Application:
passPyFlyApplication.__init__ sees the __pyfly_starter_*__ attributes,
expands every dotted key into the nested config dictionary, and
merges the result between framework defaults and the user's
pyfly.yaml. So:
- Framework default
pyfly.cqrs.enabled=false→ starter setstrue. - User
pyfly.yamlsayspyfly.cqrs.enabled=false→ user wins (explicit user choice always beats the bundle).
This mirrors .NET's services.AddFireflyCore(...):
from pyfly.core.application import PyFlyApplication
from pyfly.starters.core import register_core_stack
@pyfly_application(name="my-service", scan_packages=["my_service"])
class Application: pass
app = PyFlyApplication(Application)
register_core_stack(app) # explicit registration — overrides config files
await app.startup()Imperative registration is authoritative: starter values win over
anything already in the config (including a user pyfly.yaml). The
last register_*_stack(...) call wins for a given key, matching .NET's
services.AddX(...) semantics.
Each starter re-exports the most commonly used types and decorators of its tier so a controller / service file needs only one import line.
from pyfly.starters.core import (
Autowired, Scope, component, configuration, rest_controller, service,
Command, CommandBus, CommandHandler, command_handler,
Query, QueryBus, QueryHandler, query_handler,
pyfly_application,
enable_core_stack, register_core_stack,
)from pyfly.starters.web import (
rest_controller, controller, controller_advice, exception_handler,
request_mapping, get_mapping, post_mapping, put_mapping,
patch_mapping, delete_mapping, sse_mapping,
Body, PathVar, QueryParam, Header, Cookie, File, UploadedFile, Valid,
enable_web_stack, register_web_stack,
)from pyfly.starters.domain import (
# DDD primitives
Entity, ValueObject, AggregateRoot, DomainEvent, Specification,
DomainRepository, DomainException, BusinessRuleViolation,
AggregateNotFound,
# Carried through from the core re-exports
Command, CommandHandler, command_handler,
Query, QueryHandler, query_handler,
rest_controller, service, configuration,
pyfly_application,
enable_domain_stack, register_domain_stack,
)| Java | .NET | Python |
|---|---|---|
fireflyframework-starter-core |
services.AddFireflyCore(...) |
@enable_core_stack / register_core_stack(app) |
| (web bundled in core) | (web bundled in core) | @enable_web_stack / register_web_stack(app) (new in v26.05.03) |
fireflyframework-starter-application |
services.AddFireflyApplication(...) |
@enable_application_stack / register_application_stack(app) |
fireflyframework-starter-data |
services.AddFireflyData(...) |
@enable_data_stack / register_data_stack(app) |
fireflyframework-starter-domain |
services.AddFireflyDomain(...) |
@enable_domain_stack / register_domain_stack(app) |
Starters compose. A typical pattern: stack @enable_application_stack
on top of a vendor- or product-specific decorator that adds your own
beans or property defaults. Multiple @enable_*_stack decorators on
the same class union their property dicts, with later decorators
winning on key overlaps.
@enable_web_stack # add the web tier on top of the application bundle
@enable_application_stack # base bundle: core + security + scheduling + …
@pyfly_application(name="acme-api", scan_packages=["acme"])
class Application:
pass┌─────────────────────────────────────────────────────────────┐
│ framework defaults (pyfly-defaults.yaml — most modules │
│ disabled by default for safety) │
├─────────────────────────────────────────────────────────────┤
│ starter defaults (@enable_*_stack — turns the bundle on) │
├─────────────────────────────────────────────────────────────┤
│ user pyfly.yaml (your project root + config/) │
├─────────────────────────────────────────────────────────────┤
│ profile overlays (pyfly-{profile}.yaml) │
├─────────────────────────────────────────────────────────────┤
│ environment vars (PYFLY_X_Y at read time) │
└─────────────────────────────────────────────────────────────┘
▲
each layer overrides the one above
register_*_stack(app) is the only exception — it stamps starter
values on top of everything (including the user yaml) because it's
called explicitly.