Design your Flowcore event model
Bergur DavidsenUpdated 2026-07-15
A useful event model makes ownership, permissions, subscriptions, replay, and change understandable. A poor model turns the event platform into an unstructured message bus.
Flowcore organizes event data as:
tenant → data core → flow type → event type → event
This guide focuses on the three levels your application teams design most often: data cores, flow types, and event types.
Start from the business domain, not the current codebase
Services, repositories, and teams change more frequently than business concepts. Model events around durable domain boundaries rather than today's deployment layout.
For example, an application may currently have separate checkout-api, order-worker, and fulfillment-service deployments. That does not automatically mean it needs three data cores. If those services participate in one governed commerce domain, a commerce data core with focused flow types may be easier to own and replay.
Before creating resources, identify:
- The business capabilities represented by the events
- Who owns their meaning and lifecycle
- Which applications may publish or consume them
- Which access, retention, and compliance rules apply
- Which history must be replayed together
- Whether the boundary must remain isolated from other domains
Design data cores as durable ownership boundaries
A data core is the primary container for a coherent body of event data. A good data-core boundary normally combines events with similar ownership, access policy, retention expectations, and operational lifecycle.
Reasonable examples include:
commercecustomer-supportdevice-telemetrybilling
A data core should be large enough to represent a meaningful domain but small enough to govern clearly.
Keep events together when
Use the same data core when events:
- Belong to one durable product or bounded context
- Share an owning team or governance process
- Need similar access and retention rules
- Are commonly queried, subscribed to, or replayed together
- Should use the same production safeguards
Split data cores when
Create separate data cores when:
- Ownership and administrative access differ materially
- Regulations or data residency require isolation
- Retention or deletion policy differs
- One domain must not be discoverable by another
- Operational failure or replay needs an independent boundary
- The data represents an unrelated business capability
Avoid the two common extremes
One data core per event type creates excessive administration. Policies, descriptions, manifests, and replay procedures become fragmented.
One global data core for everything makes permissions broad, ownership unclear, and replay difficult to scope.
Aim for stable bounded contexts instead.
Document data-core intent
Every production data core should have a description that answers:
- What domain does this data core represent?
- Which team owns it?
- What data belongs here?
- What data explicitly does not belong here?
- Which retention and sensitivity policy applies?
- Who approves destructive operations?
The name is a durable identifier; the description carries human context. Keep both under review.
Set safe production defaults
A v2 data-core resource includes access control and delete protection.
apiVersion: data-core.flowcore.io/v1
kind: DataCore
metadata:
name: commerce
tenant: acme-production
spec:
accessControl: private
deleteProtection: true
description: Durable commerce events owned by the Orders teamUse private unless cross-tenant access is intentionally designed and reviewed. Enable delete protection for production data cores.
Access control and IAM are related but different:
- Data-core access control determines whether the resource is private or public.
- IAM determines which identities may perform specific actions on tenant resources.
A private data core still needs least-privilege IAM. A public setting does not mean anonymous applications should receive unrestricted access.
Design flow types around related lifecycles
A flow type groups event types that belong to one domain lifecycle or closely related subject area inside a data core.
For a commerce data core, possible flow types include:
order.0payment.0fulfillment.0
Each flow type provides a stable grouping for discovery, permissions, subscriptions, Data Pathway sources, and replay.
A good flow type has:
- One clear domain purpose
- An identifiable owner
- Event types that are commonly understood together
- A lifecycle that can evolve without absorbing unrelated events
Do not create a flow type solely because one service produces the events. If service ownership changes, the domain name should still make sense.
Design event types as immutable facts
An event type names one kind of fact that has already happened.
Good names include:
order.placed.0payment.authorized.0shipment.dispatched.0customer.email-changed.0
Avoid names such as:
create-orderupdateprocess-paymentsend-to-databasewebhook-message
Commands express requested work. Events record accepted outcomes. Transport and storage names describe implementation details, not business meaning.
A consumer should be able to understand an event's meaning without knowing the producer's private database schema.
Keep events focused and useful
An event payload should communicate the fact completely enough for intended consumers without becoming a copy of every field in the producer's database.
Include:
- Stable identifiers for the subject
- The values that define the fact
- Domain time where relevant
- Correlation or causation metadata when useful
- References needed by independent consumers
Avoid:
- Access tokens and secrets
- Unbounded binary data
- Incidental database columns
- Fields whose meaning is only known inside one service
- Mutable URLs or internal implementation paths unless they are truly part of the contract
Whether to include more context or only changed fields is a domain decision. Optimize for independent interpretation and future replay, not minimum bytes alone.
Use a predictable naming convention
Names appear in code, manifests, URLs, permissions, subscriptions, dashboards, and retained history. Treat them as API contracts.
A common Flowcore convention is:
- Flow type:
domain.0 - Event type:
entity.fact.0 - Full path:
domain.0/entity.fact.0
For example:
order.0/order.placed.0
Use:
- Lowercase names
- Consistent separators
- Past-tense facts
- Explicit version suffixes
- Domain language understood outside one team
Do not mix naming conventions casually across the same data core. Pick one convention, document it, and automate checks where practical.
Version meaning, not every code change
The version suffix represents contract semantics. It does not need to change whenever implementation code changes.
A new version is normally required when old consumers cannot safely interpret the new event, for example:
- A field changes type
- A required field is removed or renamed
- The event describes a different business fact
- Units, currency, identifiers, or invariants change meaning
- Existing values receive a new interpretation
An additive optional field may remain compatible when old consumers ignore unknown fields and new consumers handle its absence. Compatibility still depends on actual producer and consumer behavior; do not assume it from syntax alone.
Model a commerce example
Suppose an organization needs order, payment, and fulfillment history.
Tenant
acme-production
Data core
commerce
Flow types and event types
commerce
├── order.0
│ ├── order.placed.0
│ ├── order.confirmed.0
│ └── order.cancelled.0
├── payment.0
│ ├── payment.authorized.0
│ └── payment.declined.0
└── fulfillment.0
├── shipment.created.0
└── shipment.dispatched.0This structure lets teams grant payment publishing separately from order publishing, subscribe to one lifecycle, or replay fulfillment without treating every commerce event as one undifferentiated stream.
Review the model with concrete questions
Before creating production resources, ask:
- Will this boundary still make sense if services or teams are reorganized?
- Can we state who owns every data core and flow type?
- Do all events in the data core share compatible governance?
- Are event names facts rather than commands?
- Can a consumer understand each event without private producer knowledge?
- Is the versioning rule clear?
- Can permissions be granted without becoming unnecessarily broad?
- Can replay be scoped to the recovery or migration we expect?
- Are sensitive fields identified before ingestion?
- Is destructive lifecycle ownership documented?
Modeling checklist
- Data cores follow durable domain and governance boundaries.
- Every data core has an owner and meaningful description.
- Production data cores are private unless sharing is intentional.
- Production data cores use delete protection.
- Flow types group coherent business lifecycles.
- Event types describe immutable facts in past tense.
- Names use one documented convention.
- Version suffixes begin with an explicit policy.
- Payloads include stable identifiers and avoid secrets.
- Replay, retention, and sensitivity requirements are understood.
Next, read Define and evolve event contracts to turn these resource names into runtime-validated payloads.