6. Modules and Assembly
A module is a release and governance boundary. A Blueprint is a selected set of modules. Bootstrap produces the effective graph consumed by generators and runtimes.
Declare bounded modules
final directoryModule = Module(
name: 'directory',
title: 'Directory',
schema: 'directory',
version: '1.0.0',
entities: [companyEntity, siteEntity, userEntity],
exports: const [
EntityExport('company'),
EntityExport('site'),
EntityExport('user'),
],
);
final opsModule = Module(
name: 'ops',
title: 'Operations',
schema: 'ops',
version: '1.0.0',
entities: [areaEntity, equipmentEntity],
dependsOn: const ['directory'],
);
final blueprint = Blueprint(
name: 'pharma_portfolio',
version: '1.0.0',
modules: [directoryModule, opsModule],
);One Postgres schema per module is the normal posture. Modules can release at different versions even though one Blueprint revision selects them together.
Cross-module access is explicit
For ops.area.site_id to target directory.site:
- Directory exports
site. - Ops lists
directoryindependsOn. - The reference/relationship targets the stable type
directory.site. - The dependency graph remains acyclic.
Do not bypass the boundary with an unchecked string or an application-only join.
Exports are typed by intent:
exports: const [
EntityExport('user'),
DerivedValueExport('user.identity.is_active'),
TriggerExport('user.lifecycle.deactivate'),
]Contribute facets without redefining an entity
An Ops package may add a qualification facet to directory.user:
final userOpsDescriptor = EntityDescriptor(
target: 'directory.user',
aspect: 'ops',
title: 'Operations qualification',
priority: 100,
facets: const [
Facet(
name: 'ops_qualification',
fields: [
BooleanField(
'gmp_qualified',
title: 'GMP Qualified',
),
],
ui: FacetUI(
kind: UISectionKind.detail,
order: 300,
),
),
],
);
final opsModule = Module(
// ...
dependsOn: const ['directory'],
descriptors: [userOpsDescriptor],
);EntityDescriptor layers facets, aggregates, and entity UI posture onto the same stable entity type. It does not mint a competing ops.user.
Bootstrap is the link phase
final findings = BlueprintValidator.validate(blueprint);
if (findings.isNotEmpty) {
throw StateError(findings.join('\n'));
}
final EffectiveBlueprint effective = blueprint.bootstrap();
final EffectiveEntity? user = effective.entity('directory.user');
print(user?.entity.facets.map((facet) => facet.name));
print(user?.originGraph);Bootstrap:
- orders descriptor contributions by priority and declaration order;
- merges facets, aggregates, and UI descriptors;
- creates the effective action catalog;
- retains an origin graph explaining where every contribution came from.
Generators should consume the effective model, normally by calling APIs that bootstrap internally.
One Blueprint owns domain and application surfaces
Blueprint declares domain semantics and application surfaces together. BlueprintDescriptorSet is an internal contribution mechanism that assembles back into that one root:
final set = BlueprintDescriptorSet([
directoryDescriptor,
opsDescriptor,
]);
final blueprint = set.blueprint(
name: 'ops',
title: 'Operations',
version: '1.0.0',
);The declaration remains usable by a server with no Flutter dependency. Its application section adds routes, collections, editors, dashboards, shell, and client extension references without introducing another Blueprint type.
Label production releases and lower environments differently
The app Blueprint can declare environment-aware header identity without hard-coding deployment rules into a Flutter header:
const deployment = DeploymentUI(
enabled: true,
productionHeaderLabels: [
DeploymentValue.releaseVersion,
],
nonProductionHeaderLabels: [
DeploymentValue.environment,
],
windowTitleLabels: [
DeploymentValue.application,
DeploymentValue.organization,
DeploymentValue.environment,
],
);The host supplies the active DeploymentContext. The runtime then places the effective label beside the app identity:
- production shows the released version, such as
2.4.1, and omits the redundantProductionenvironment badge by default; - development, test, UAT, training, validation, staging, and sandbox show their environment label instead of presenting a lower build as a production release;
headerLabelsremains the fallback when an environment-specific list is not declared.
This information is display and cache-partition metadata. The server still derives tenant authority, entitlements, and permissions from the authenticated request.
Checkpoint
Create three modules:
directory: company, site, user;access: role, permission, assignment; depends on Directory;ops: area and equipment; depends on Directory and Access.
Export only the entities and triggers another module is allowed to consume. Run validation and inspect the effective origin graph.
Next: Database, security, and seed hints.
References: Entity descriptors · Developer guide.