|
| 1 | +# Starters — Layered Bundles |
| 2 | + |
| 3 | +Starters are **opinionated bundles** that activate every framework |
| 4 | +module a given service tier needs in a single decorator. They mirror |
| 5 | +``org.fireflyframework.starter.*`` (Java) and |
| 6 | +``FireflyFramework.Starter.*`` (.NET) so a service that's been written |
| 7 | +on one platform reads the same way on every other. |
| 8 | + |
| 9 | +## Available starters |
| 10 | + |
| 11 | +| Starter | Activates | Use it for | |
| 12 | +|---------|-----------|-----------| |
| 13 | +| **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. | |
| 14 | +| **Web** (`enable_web_stack`) | web, server, observability, metrics, tracing, actuator (+ metrics), resilience | Pure HTTP/REST APIs that don't need EDA, CQRS or cache. | |
| 15 | +| **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. | |
| 16 | +| **Data** (`enable_data_stack`) | core stack + relational, document, HTTP client, scheduling, resilience | Data ingestion / enrichment / batch services. | |
| 17 | +| **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. | |
| 18 | + |
| 19 | +## Two ways to use a starter |
| 20 | + |
| 21 | +### 1. Declarative (preferred) — decorate the application class |
| 22 | + |
| 23 | +```python |
| 24 | +from pyfly.core import pyfly_application |
| 25 | +from pyfly.starters.domain import enable_domain_stack |
| 26 | + |
| 27 | +@enable_domain_stack |
| 28 | +@pyfly_application(name="my-service", scan_packages=["my_service"]) |
| 29 | +class Application: |
| 30 | + pass |
| 31 | +``` |
| 32 | + |
| 33 | +`PyFlyApplication.__init__` sees the `__pyfly_starter_*__` attributes, |
| 34 | +expands every dotted key into the nested config dictionary, and |
| 35 | +**merges the result between framework defaults and the user's |
| 36 | +`pyfly.yaml`**. So: |
| 37 | + |
| 38 | +- Framework default `pyfly.cqrs.enabled=false` → starter sets `true`. |
| 39 | +- User `pyfly.yaml` says `pyfly.cqrs.enabled=false` → user wins |
| 40 | + (explicit user choice always beats the bundle). |
| 41 | + |
| 42 | +### 2. Imperative (explicit) — call `register_*_stack(app)` |
| 43 | + |
| 44 | +This mirrors .NET's `services.AddFireflyCore(...)`: |
| 45 | + |
| 46 | +```python |
| 47 | +from pyfly.core.application import PyFlyApplication |
| 48 | +from pyfly.starters.core import register_core_stack |
| 49 | + |
| 50 | +@pyfly_application(name="my-service", scan_packages=["my_service"]) |
| 51 | +class Application: pass |
| 52 | + |
| 53 | +app = PyFlyApplication(Application) |
| 54 | +register_core_stack(app) # explicit registration — overrides config files |
| 55 | +await app.startup() |
| 56 | +``` |
| 57 | + |
| 58 | +Imperative registration is **authoritative**: starter values win over |
| 59 | +anything already in the config (including a user `pyfly.yaml`). The |
| 60 | +last `register_*_stack(...)` call wins for a given key, matching .NET's |
| 61 | +`services.AddX(...)` semantics. |
| 62 | + |
| 63 | +## Re-exports — single import line per layer |
| 64 | + |
| 65 | +Each starter re-exports the most commonly used types and decorators of |
| 66 | +its tier so a controller / service file needs only one import line. |
| 67 | + |
| 68 | +### Core layer |
| 69 | + |
| 70 | +```python |
| 71 | +from pyfly.starters.core import ( |
| 72 | + Autowired, Scope, component, configuration, rest_controller, service, |
| 73 | + Command, CommandBus, CommandHandler, command_handler, |
| 74 | + Query, QueryBus, QueryHandler, query_handler, |
| 75 | + pyfly_application, |
| 76 | + enable_core_stack, register_core_stack, |
| 77 | +) |
| 78 | +``` |
| 79 | + |
| 80 | +### Web layer |
| 81 | + |
| 82 | +```python |
| 83 | +from pyfly.starters.web import ( |
| 84 | + rest_controller, controller, controller_advice, exception_handler, |
| 85 | + request_mapping, get_mapping, post_mapping, put_mapping, |
| 86 | + patch_mapping, delete_mapping, sse_mapping, |
| 87 | + Body, PathVar, QueryParam, Header, Cookie, File, UploadedFile, Valid, |
| 88 | + enable_web_stack, register_web_stack, |
| 89 | +) |
| 90 | +``` |
| 91 | + |
| 92 | +### Domain layer |
| 93 | + |
| 94 | +```python |
| 95 | +from pyfly.starters.domain import ( |
| 96 | + # DDD primitives |
| 97 | + Entity, ValueObject, AggregateRoot, DomainEvent, Specification, |
| 98 | + DomainRepository, DomainException, BusinessRuleViolation, |
| 99 | + AggregateNotFound, |
| 100 | + # Carried through from the core re-exports |
| 101 | + Command, CommandHandler, command_handler, |
| 102 | + Query, QueryHandler, query_handler, |
| 103 | + rest_controller, service, configuration, |
| 104 | + pyfly_application, |
| 105 | + enable_domain_stack, register_domain_stack, |
| 106 | +) |
| 107 | +``` |
| 108 | + |
| 109 | +## Cross-language correspondence |
| 110 | + |
| 111 | +| Java | .NET | Python | |
| 112 | +|------|------|--------| |
| 113 | +| `fireflyframework-starter-core` | `services.AddFireflyCore(...)` | `@enable_core_stack` / `register_core_stack(app)` | |
| 114 | +| (web bundled in core) | (web bundled in core) | `@enable_web_stack` / `register_web_stack(app)` *(new in v26.05.03)* | |
| 115 | +| `fireflyframework-starter-application` | `services.AddFireflyApplication(...)` | `@enable_application_stack` / `register_application_stack(app)` | |
| 116 | +| `fireflyframework-starter-data` | `services.AddFireflyData(...)` | `@enable_data_stack` / `register_data_stack(app)` | |
| 117 | +| `fireflyframework-starter-domain` | `services.AddFireflyDomain(...)` | `@enable_domain_stack` / `register_domain_stack(app)` | |
| 118 | + |
| 119 | +## Composing starters |
| 120 | + |
| 121 | +Starters compose. A typical pattern: stack `@enable_application_stack` |
| 122 | +*on top of* a vendor- or product-specific decorator that adds your own |
| 123 | +beans or property defaults. Multiple `@enable_*_stack` decorators on |
| 124 | +the same class union their property dicts, with later decorators |
| 125 | +winning on key overlaps. |
| 126 | + |
| 127 | +```python |
| 128 | +@enable_web_stack # add the web tier on top of the application bundle |
| 129 | +@enable_application_stack # base bundle: core + security + scheduling + … |
| 130 | +@pyfly_application(name="acme-api", scan_packages=["acme"]) |
| 131 | +class Application: |
| 132 | + pass |
| 133 | +``` |
| 134 | + |
| 135 | +## Property layering recap |
| 136 | + |
| 137 | +``` |
| 138 | +┌─────────────────────────────────────────────────────────────┐ |
| 139 | +│ framework defaults (pyfly-defaults.yaml — most modules │ |
| 140 | +│ disabled by default for safety) │ |
| 141 | +├─────────────────────────────────────────────────────────────┤ |
| 142 | +│ starter defaults (@enable_*_stack — turns the bundle on) │ |
| 143 | +├─────────────────────────────────────────────────────────────┤ |
| 144 | +│ user pyfly.yaml (your project root + config/) │ |
| 145 | +├─────────────────────────────────────────────────────────────┤ |
| 146 | +│ profile overlays (pyfly-{profile}.yaml) │ |
| 147 | +├─────────────────────────────────────────────────────────────┤ |
| 148 | +│ environment vars (PYFLY_X_Y at read time) │ |
| 149 | +└─────────────────────────────────────────────────────────────┘ |
| 150 | + ▲ |
| 151 | + each layer overrides the one above |
| 152 | +``` |
| 153 | + |
| 154 | +`register_*_stack(app)` is the only exception — it stamps starter |
| 155 | +values **on top** of everything (including the user yaml) because it's |
| 156 | +called explicitly. |
0 commit comments