Component Order Fulfillment
Build a single order-management service from reusable, explicitly wired components that validate, enrich, reserve, and fulfill an order.
Examplecomponentserviceexpressionorder-managementcomposition
🌅 Horizon
Example at a Glance
Overview
An order-management boundary must turn an incoming order into a fulfillment result without making its API handler own every business decision. This example separates that work into reusable components: order processing, inventory reservation, and fulfillment. Each component declares a small public contract and composes its internal expressions or nested components explicitly.
Architecture
The service coordinates public use cases; components own reusable business capabilities and reveal only their declared input and output ports.
What It Demonstrates
@component— public typed input/output ports and explicit internal wiring.- Component composition —
OrderFulfillmentComponentuses nested processing and inventory-reservation components. - Inter-component wiring — fulfillment connects nested public ports into one typed business flow.
@expression— tax calculation, warehouse selection, delivery estimation, and result assembly remain independently testable.@serviceand@api— the public order endpoints delegate to components rather than duplicating business logic.
Expected Result
A submission receives a server-generated order ID, becomes a
ProcessedOrder, and reserves inventory through nested
components. The UI renders the complete response: the original order,
enrichment values, reservation, and final status.
🧭 Voyage
1. Problem and Constraints
The system receives commercial order details: item name, source, destination, quantity, unit price, and currency. It must generate an ID, calculate tax for its route, choose a warehouse, estimate a delivery date, reserve stock, and report the fulfillment outcome. The API and service must not duplicate the internal pipeline.
- Components expose only business-level input and output ports.
- Nested components cannot leak their private aliases or dependencies.
- Expressions implement focused calculations, not orchestration.
- The example deliberately uses one service so component boundaries remain the central lesson.
2. Domain Contracts
OrderSubmission is the inbound business request. The
fulfillment boundary generates its canonical Order ID. Its source and
destination determine tax and delivery behavior; its quantity and unit
price determine inventory and total calculations. Currency remains part
of the commercial order contract throughout the processing pipeline. The processing
component produces ProcessedOrder, preserving the original
order while adding tax, selected warehouse, estimated delivery date,
and total amount. Fulfillment returns the complete
ProcessedOrder, including reservation and status.
OrderSubmission
name: String
source: String
destination: String
quantity: Float
unitPrice: Float
currency: String
Order
id: String
name: String
source: String
destination: String
quantity: Float
unitPrice: Float
currency: String
ProcessedOrder
order: Order
tax: Float
warehouseId: String
deliveryDate: DateTime
totalAmount: Float
reservation: InventoryReservation
status: String
3. Component Design
The model uses three components. Order fulfillment is the service-facing capability; processing and reservation are nested within it because they are implementation details of one fulfillment use case, not separate service APIs.
| Component | Public contract | Responsibility |
|---|---|---|
OrderProcessingComponent | Order → ProcessedOrder | Chains tax, warehouse, and delivery expressions, then assembles the enriched order. |
OrderFulfillmentComponent | OrderSubmission → ProcessedOrder | Generates the order ID, then composes processing and reservation. |
InventoryReservationComponent | ProcessedOrder → ProcessedOrder | Nested capability that adds reservation details and final status. |
4. Build the Reusable Processing Pipeline
OrderProcessingComponent is the main data-enrichment
example. It accepts one Order, calls the three focused
expressions, and exposes a single ProcessedOrder output.
Its connection graph documents the dependency order without forcing the
service to know individual calculation aliases.
5. Nest Reservation Within Fulfillment
OrderFulfillmentComponent uses both
OrderProcessingComponent and
InventoryReservationComponent through their declared
public ports. The outer component may connect to those ports, but
cannot connect to either nested component's private expressions. This
demonstrates composition with encapsulation.
OrderFulfillmentComponent
input submission: OrderSubmission
output processedOrder: ProcessedOrder
use expression CreateOrder as create
use component OrderProcessingComponent as processing
use component InventoryReservationComponent as inventory
connect submission -> create
connect create -> processing.order
connect processing.processedOrder -> inventory.processedOrder
connect inventory.completedOrder -> processedOrder
6. Compose Components at the Service Boundary
OrderManagementService implements the public API and uses
OrderFulfillmentComponent. Its submitOrder
endpoint delegates to that single public port. The component owns the
inter-component wiring, so processing and reservation remain reusable
while the service declares one clear application use case.
7. Example Structure
0020-component-order-fulfillment/
├── 00-cof-info.ocn
├── 10-cof-datatype.ocn
├── 20-cof-api.ocn
├── 30-cof-config.ocn
├── 40-cof-expression.ocn
├── 46-cof-component.ocn
├── 50-cof-service.ocn
├── example.md
└── example-info/
└── example-info.html
8. What to Inspect
- Open the component graph and follow
Order → ProcessedOrderthrough the three processing expressions. - Verify that the fulfillment component owns the edge to the nested inventory component.
- Inspect the service graph to see the API endpoint connect to a component port rather than individual expressions.
- Inspect the Flow view to confirm the API, service-owned component calls, and typed ports form one traceable path.
9. Validate, Generate, and Run
- Open the
0020-component-order-fulfillmentfolder as one Ocean bundle and run Validate. The bundle should report no diagnostics. - Run Generate with the Go backend target. The generated service contains one file for each of the three components.
- In the generated project root, run
./bootstrap.sh build. It formats the project, generates REST documentation, resolves dependencies, and buildsorder-management-service.
10. Experiments and Extensions
- Add a second warehouse-selection policy and route it through a mapper expression.
- Add a failed-reservation branch that releases a provisional shipment.
- Move inventory reservation behind a broker request-response contract while preserving the fulfillment component's public ports.
- Reuse
OrderProcessingComponentfrom a batch-import service to show that components are not tied to one API.
Executable model
<\> Implementation
Explore the runnable model by responsibility, then select a file to inspect its complete source.
# @ocean-meta-start
# tags:
# - component
# - order-management
# - fulfillment
# perspective:
# feature: component-order-fulfillment
# service: order-management-service
# @ocean-meta-end
@info
ComponentOrderFulfillment
name = Component Order Fulfillment
version = 1.0.0
description = A focused order-management service that composes reusable processing and inventory components into one fulfillment capability.
# @ocean-meta-start
# tags:
# - datatype
# - order-management
# perspective:
# feature: component-order-fulfillment
# service: order-management-service
# @ocean-meta-end
@datatype
# The caller submits commercial data only. The service assigns the canonical
# order ID at its boundary, so clients can never choose or collide with it.
OrderSubmission
name : String
source : String
destination : String
quantity : Float
unitPrice : Float
currency : String
# The canonical order is created by the fulfillment component before it enters
# the reusable processing pipeline.
Order
id : String
name : String
source : String
destination : String
quantity : Float
unitPrice : Float
currency : String
# Intermediate contracts make each processing decision observable in the
# component graph and retain the original order throughout the pipeline.
TaxedOrder
order : Order
tax : Float
totalAmount : Float
WarehouseSelectedOrder
order : Order
tax : Float
totalAmount : Float
warehouseId : String
ProcessedOrder
order : Order
tax : Float
totalAmount : Float
warehouseId : String
deliveryDate : DateTime
reservation : InventoryReservation
status : String
InventoryReservation
orderId : String
warehouseId : String
quantity : Float
deliveryDate : DateTime
# @ocean-meta-start
# tags:
# - api
# - rest-api
# perspective:
# feature: component-order-fulfillment
# service: order-management-service
# @ocean-meta-end
@api
OrderManagementApi style:rest
engine = gin
configType = ApiConfig
tags = external, order-management
version = 1.0.0
description = Submit an order for processing and fulfillment.
basePath = /orders
generateSwagger = true
post /submit submitOrder(order:OrderSubmission) : ProcessedOrder
# @ocean-meta-start
# tags:
# - config
# perspective:
# feature: component-order-fulfillment
# service: order-management-service
# @ocean-meta-end
@config
@import config O.log.config@1.0.0 as LogConfig
OrderManagementConfig
apiConfig : ApiConfig
logConfig : LogConfig
ApiConfig
port : Int (default=8080)
UiConfig
port : Int (default=8081)
logConfig : LogConfig
# @ocean-meta-start
# tags:
# - expression
# - order-management
# perspective:
# feature: component-order-fulfillment
# service: order-management-service
# @ocean-meta-end
@expression
# The boundary owns ID assignment. The caller sends an OrderSubmission, while
# reusable components always receive an Order with a generated canonical ID.
CreateOrder
input : submission:OrderSubmission
output : order:Order
logic
order.id = GetUUID()
order.name = submission.name
order.source = submission.source
order.destination = submission.destination
order.quantity = submission.quantity
order.unitPrice = submission.unitPrice
order.currency = submission.currency
# Processing is deliberately expressed as three small, independently reusable
# decisions. The component below owns their composition.
CalculateOrderTax
input : order:Order
output : taxed:TaxedOrder
logic
taxed.order = order
taxed.tax = order.quantity * order.unitPrice * 0.21
taxed.totalAmount = order.quantity * order.unitPrice + taxed.tax
ChooseWarehouse
input : taxed:TaxedOrder
output : selected:WarehouseSelectedOrder
logic
selected.order = taxed.order
selected.tax = taxed.tax
selected.totalAmount = taxed.totalAmount
selected.warehouseId = "nl-amsterdam"
EstimateDeliveryDate
input : selected:WarehouseSelectedOrder
output : processed:ProcessedOrder
logic
processed.order = selected.order
processed.tax = selected.tax
processed.totalAmount = selected.totalAmount
processed.warehouseId = selected.warehouseId
processed.deliveryDate = nowUTC()
ReserveInventory
input : processed:ProcessedOrder
output : completed:ProcessedOrder
logic
var reservation InventoryReservation
completed.order = processed.order
completed.tax = processed.tax
completed.totalAmount = processed.totalAmount
completed.warehouseId = processed.warehouseId
completed.deliveryDate = processed.deliveryDate
reservation.orderId = processed.order.id
reservation.warehouseId = processed.warehouseId
reservation.quantity = processed.order.quantity
reservation.deliveryDate = processed.deliveryDate
completed.reservation = reservation
completed.status = "reserved"
# @ocean-meta-start
# tags:
# - component
# - nested-component
# - order-management
# perspective:
# feature: component-order-fulfillment
# service: order-management-service
# @ocean-meta-end
@component
# A reusable, inspectable enrichment pipeline. Its single public contract
# hides the three calculation aliases from any host component or service.
OrderProcessingComponent
@perspectives: version:1.0.0, lifestyle:stable
input : order:Order
output : processedOrder:ProcessedOrder
use expression CalculateOrderTax as tax
use expression ChooseWarehouse as warehouse
use expression EstimateDeliveryDate as delivery
connect order -> tax
connect tax -> warehouse
connect warehouse -> delivery
connect delivery -> processedOrder
# Reservation is independently reusable, but its hosting component decides
# whether it is part of a larger fulfillment use case.
InventoryReservationComponent
@perspectives: version:1.0.0, lifestyle:stable
input : processedOrder:ProcessedOrder
output : completedOrder:ProcessedOrder
use expression ReserveInventory as reserve
connect processedOrder -> reserve
connect reserve -> completedOrder
# This is the service-facing capability. It assigns an ID, composes the other
# components only through their public ports, and returns the entire enriched
# order including the reservation outcome.
OrderFulfillmentComponent
@perspectives: version:1.0.0, lifestyle:stable
input : submission:OrderSubmission
output : processedOrder:ProcessedOrder
use expression CreateOrder as create
use component OrderProcessingComponent as processing
use component InventoryReservationComponent as inventory
connect submission -> create
connect create -> processing.order
connect processing.processedOrder -> inventory.processedOrder
connect inventory.completedOrder -> processedOrder
# @ocean-meta-start
# tags:
# - service
# - component-host
# perspective:
# feature: component-order-fulfillment
# service: order-management-service
# @ocean-meta-end
@service
OrderManagementService
@perspectives: version:1.0.0, lifestyle:stable
use config OrderManagementConfig as cfg
impl api OrderManagementApi as api on cfg.apiConfig.port
use component OrderFulfillmentComponent as fulfillment
# The API delegates one business use case to one public component port.
# Processing and inventory remain reusable nested capabilities.
connect api.submitOrder -> fulfillment.submission
# @ocean-meta-start
# tags:
# - dashboard
# - component
# - order-management
# perspective:
# feature: component-order-fulfillment
# service: order-management-ui
# @ocean-meta-end
@dashboard
OrderFulfillmentDashboard
title: Order Fulfillment
subtitle: Submit an order and inspect the composed fulfillment outcome.
layout: SingleColumnLayout
Widget PipelineGuide of type Text
title = One API call, three reusable components
subtitle = Processing enriches the order; reservation and fulfillment complete it.
content = <p>The Order Management API delegates <b>submitOrder</b> to the public port of <b>OrderFulfillmentComponent</b>. That component composes Order Processing and Inventory Reservation through their typed ports. The UI remains coupled only to the API contract.</p>
Widget SubmitOrderForm of type Form
title = Submit an order for fulfillment
datatype = OrderSubmission
resultView = ProcessedOrderResult
buttons:
submit : Fulfill order
cancel : Reset
Widget ProcessedOrderResult of type JsonView
title = Processed order
subtitle = Full result from the reusable fulfillment pipeline
datatype = ProcessedOrder
emptyMessage = Submit an order to inspect the complete response.
copy = true
Widget PipelineSummary of type Text
title = Expected outcome
subtitle = The response displays the full enriched and reserved order.
content = <p>The service generates the order ID, calculates tax, selects the modeled Amsterdam warehouse, estimates delivery, and reserves inventory. The response contains the original order, all enrichment values, reservation details, and the final <b>reserved</b> status.</p>
AboutComponentDashboard
title: About Components
subtitle: Reusable in-process business capabilities.
layout: SingleColumnLayout
Widget ComponentGuide of type Text
title = Why this is a component example
subtitle = Services own transport; components own reusable behavior.
content = <p>OrderProcessingComponent owns tax, warehouse, and delivery decisions. InventoryReservationComponent owns reservation details. OrderFulfillmentComponent nests both through public ports, so neither the API handler nor the service needs to know their internal aliases.</p>
# @ocean-meta-start
# tags:
# - ui
# - dashboard
# - component
# perspective:
# feature: component-order-fulfillment
# service: order-management-ui
# @ocean-meta-end
@ui
OrderManagementUi
@perspectives: version:1.0.0, lifestyle:stable
framework: htmx
styling: bootstrap
template: go-html-template
backend: go-gin
static: static
configType: UiConfig
nav Fulfillment dashboard = OrderFulfillmentDashboard
nav About dashboard = AboutComponentDashboard
header title = Component Order Fulfillment
header subtitle = A typed, reusable order-processing pipeline
header align = center
footer title = Built with Ocean
footer subtitle = Component composition example
footer align = center
use dashboard OrderFulfillmentDashboard as fulfillment
use dashboard AboutComponentDashboard as about
use api OrderManagementApi as orders
connect fulfillment.SubmitOrderForm.submit -> orders.submitOrder
# @ocean-meta-start
# tags:
# - deployment
# - component
# perspective:
# feature: component-order-fulfillment
# service: shared
# @ocean-meta-end
@deploy
Name: ComponentOrderFulfillmentDeploy
OrderManagementDeploy
service OrderManagementService
replica 1
export 9090:OrderManagementService.api
OrderManagementUiDeploy
service OrderManagementUi
replica 1
export 9091:OrderManagementUi.config
dependsOn OrderManagementDeploy