Architecture contract¶
This page fixes the layout of the repository and the names it uses. Architecture rules states how the layout is checked.
The package moves from a layout by source (contract for the CCNL,
tax for the statutory tables, payroll for the computation) to a layout
by concept: one root per concept (sickness, contribution, irpef), with
the CCNL rules, the statutory tables and the computation of that concept
below it.
The contract has four pages:
- this page: principles, decisions, the production tree, the concepts, the roles and the dependency tiers;
- Target trees: the data,
demo,scripts, examples and test trees; - Production map: the target of every current production module;
- Migration notes: the namespace package spike, the path-keyed data a move must update, and the amendments.
Principles¶
- A directory is a plain domain name: a concept, or a subdomain of one. A
file is exactly a technical role. No
<role>_<subject>.py, no<subject>_<role>.py: two files with one role in one domain go one domain level deeper (contribution/pension_fund/decision/services.py). - Depth is not limited: it follows the domain.
- Every directory name is listed in the "Directory names" section of the glossary; right below the package root only a concept. One concept uses one term in production code, knowledge data, scripts and tests; a name means one thing wherever it appears.
- No file name starts with an underscore, except the package root
src/ccnl_engine/__init__.pyand the docstring-only package markers. - Every source directory keeps a package marker: an
__init__.pyholding only a docstring. The documentation tooling (griffe, behind mkdocstrings) does not load a namespace directory inside a regular package. Test directories carry no marker: pytest imports them by path. - A leaf directory holds only the roles it needs. No empty file is created for symmetry.
- A move keeps the persisted identities: ruleset ids (
tax/2026/...,inps/2026/...),ccnl_slugvalues and the tags of the state codec do not follow the directories.
Decisions¶
| Topic | Decision |
|---|---|
| Package root | src/ccnl_engine/__init__.py stays, with its docstring and its re-exports. |
| Public API | The root and the namespaces ccnl_engine.inputs, ccnl_engine.events, ccnl_engine.results and ccnl_engine.catalog keep their import paths, as pinned by tests/integration/ccnl_engine/api/test_public_exports.py. |
catalog |
Becomes the package catalog/: its __init__.py holds the public namespace, like the package root, the only other __init__.py allowed to hold code; the catalog modules (loaders, models, services) sit next to it. |
| Root modules | api, inputs, results, events, errors, version, plus the roles types (today primitives) and validators (today validation); validation_collection becomes collection/validators.py. No alias: nothing outside the package imports the old names. |
| Deep imports | Paths below the five public modules are not public. Moves carry no shim and no deprecation alias. |
| Knowledge data | The JSON bundle stays in one tree, knowledge/, with its datasets named after the concepts that read them. |
Production tree¶
src/ccnl_engine/
├── __init__.py api.py inputs.py events.py results.py
├── errors.py types.py validators.py version.py
├── catalog/ collection/
├── agreement/ the CCNL document: identity, metadata, validity, coverage
├── compensation/ levels, allowances, pay elements, proration, renewal minimum
├── pay_item/ pay item variants, treatment, rounding, policy
├── employment/ employer/ apprenticeship/ seniority/
├── working_time/ absence/ sickness/ extra_month/ termination/
├── contribution/ INPS: rates, bases, ceiling, NASpI, domestic, apprentices
├── fund/ pension_fund/ severance/ end_of_service/
├── irpef/ tax_credit/ surtax/ regime/ variable_pay/ family/
├── withholding/ ledger/ event/ balance/
├── decision/ assurance/ reconciliation/ limitation/ capability/
├── run/ period/ year/ default/ calendar/ amount/
├── knowledge/ the JSON datasets and their manifest, data only
├── bundle/ sector/ readers of the bundle; the rules of a tax sector and year
├── provenance/ comparison/
The subdomains of each concept are in Production map.
Concepts¶
| Concept | Holds |
|---|---|
agreement |
A CCNL as a document: root model, identity, metadata, validity and time series, coverage notes |
catalog |
The public catalog: bundled contracts, discovery, ruleset readiness |
compensation |
Levels and allowances, the pay elements of a level, proration, hourly pay and its percentages, renewal minimum, raccordo and provincial elements |
pay_item |
Pay item variants, their treatment, selection policy and rounding |
employment, employer |
The employment relationship and its facts; the employer profile |
apprenticeship, seniority |
Apprentice tracks and pay; seniority increments and their ladder |
working_time, absence |
Working time, overtime and leave; absences |
sickness |
Episodes, sick days, CCNL integration, INPS indemnity, cumulation |
extra_month |
Tredicesima and quattordicesima: entitlement, schedule, ratei, settlement, liquidation |
termination |
Termination events and their handlers |
contribution |
INPS rates, bases, ceiling, minimum base, NASpI, domestic work, apprentices, ENAM |
fund, pension_fund |
Contractual funds of the CCNL; the complementary pension fund |
severance, end_of_service |
TFR: accrual, provision, revaluation, relief, public reduction; the public end-of-service fund |
irpef, tax_credit, surtax |
IRPEF; credits paid on the payslip and their recovery; regional and municipal surtax |
regime, variable_pay, family |
Substitute tax regimes; variable pay and benefits; art. 12 TUIR deductions |
withholding, ledger, event |
What a run withholds; ledger entries and remittance; work events |
balance |
What a run opens and closes with: competence, tax cash, YTD accounts, obligations, opening balances, the state codec |
decision, assurance, reconciliation, limitation |
Calculation decisions and the decisions of a run; payability; result invariants; model limitations |
capability |
Capability registry, requirements, traces and reports |
run, period, year, default |
The run identity and its month; one payroll run; competence and tax years; defaults of the public inputs |
calendar, amount |
The payroll calendar: schedules, payment identity, tax year of a payment; the amounts of a run |
knowledge |
The JSON data bundle and its manifest, data only |
bundle, sector |
Readers of the bundle: manifest, resources, hash and label checks; the rules of a tax sector and year |
provenance, comparison |
Ruleset and source provenance; the rules diff of a CCNL |
collection |
Validators of the public collections |
Technical roles¶
| Role file | Holds |
|---|---|
models.py, types.py |
State and value objects; enums and type aliases |
inputs.py, requests.py, results.py |
Contracts at a boundary: facts a caller passes, requests, results |
policies.py, rules.py |
Pure decisions: configurable policies, statutory or contractual rules |
services.py, handlers.py |
Use cases and orchestration; one handler per event family |
ports.py, repositories.py |
Abstract dependencies and the adapters that implement them |
loaders.py, serializers.py |
Reading bundled data; persistence and textual forms |
validators.py |
Invariants and validation |
facade.py |
The single public entry of a domain |
A role that outgrows the structural limits moves part of its content into a subdomain directory with the same role file; it never takes a suffix.
Dependency tiers¶
Every concept sits in one tier. A module imports at runtime only concepts of its own tier or of a lower one; cycles inside a tier are allowed.
| Tier | Concepts |
|---|---|
| orchestration | api, inputs, events, results, catalog, assurance, capability, comparison, default, period, reconciliation, year |
| computation | agreement, amount, bundle, calendar, decision, knowledge, sector, limitation, balance, absence, apprenticeship, compensation, contribution, employer, employment, end_of_service, event, extra_month, family, fund, irpef, ledger, pay_item, pension_fund, regime, seniority, severance, sickness, surtax, tax_credit, termination, variable_pay, withholding, working_time |
| norms | types, validators, errors, version, collection, provenance, run |
The migration resolved the imports from computation into orchestration by
moving the shared helpers and types to the tier of their users
(pay_item/services.py, decision/rules.py, calendar/, amount/). Two
imports remain, by design: the INPS and
seniority decisions cite the payable rules of the run through the capability
rule lookup.
Inside a concept the role direction holds:
facade -> handlers / services -> ports / repositories / loaders / serializers
-> policies / rules / validators -> models / types / inputs / requests / results
Pure roles perform no I/O, and only the package root and the public
namespaces import api.py or a facade.py.