Compose Products and Blocks
Use this guide when you need to compose Blocks and share records across Workspace. Give each installed definition one owner. Consumers depend on that owner.
Before you start
Before composing Products and Blocks, name the work, its users and their shared records. Inspect existing source before adding definitions. Workspace owns the release and install lifecycle.
Choose an owner
A Block lists the resource handles it owns and can contain child Blocks. Export it as a BlockContribution to reuse it. A Product adds versioning and installation around one root Block, and publication fixes the publisher afterward. A child Block has no separate release or install.
| Need | Composition |
|---|---|
| Reuse code in another assembly | Import or copy a public Block and retain its origin. |
| Share the same installed records and behavior | Give one Product ownership and add exact dependencies from consumers. |
| Use entity-defining code in Products installed together | Keep those definitions in one owner. Importing them into both consumers duplicates ownership. |
A second screen alone does not require another Product. A shared foundation earns an owner when named consumers need the same records or behavior.
Compose the root
Import the real capability Block and compose the Product with composeProduct. Publication metadata is not a source input. Preview and publish fix it.
import { composeProduct } from '@merulan/cdk/product';
import { followThrough } from './follow-through/block.ts';
export const composition = composeProduct({
identity: { key: 'sales', version: '1.0.0', name: 'Sales' },
install: { namespacePolicy: 'defaulted', duplicatePolicy: 'fail' },
root: { id: 'sales', name: 'Sales', children: [followThrough] },
});
export const product = composition.source;A Workspace selects exact releases and supplies declared configuration. Consumers pin the shared owner's exact release digest. A copy of the owner's schema is not a dependency.
Map the work
APQC classifies the work a capability supports. A Sales Product can map to APQC 3.5, with Blocks for its included processes. A category does not require every process beneath it.
State the correspondence, scope and construct evidence. Publish only mapping claims that the pinned adapter accepts. A shared schema need not claim to perform a whole business process. Other frameworks can classify the same Product.
Keep dependencies acyclic
A Shared Context Product can own people, companies, Meetings and participation. Sales owns sales records and Meeting-to-sales links. Projects owns work records and Meeting-to-Project links. Both depend on Shared Context. The foundation has no dependency back to either consumer.
Keep inference with the consumer that reads its inputs. A Pipeline using sales records to shortlist Projects can stay in Sales. Projects can still own the Project relationship definition.
When it fails
workspace.structure.orphan_block
The Block has no owning path or runtime import. Restore its import beneath the owner. A type-only import does not establish ownership.
workspace.closure.conflict
The exact dependency closure is incoherent. Check release coordinates and digests, then compile the full closure again.
Next steps
Extract a shared Product when a second consumer changes the boundary. Check syntax in the CDK reference.