ODS Core
The Open Domain Specification (ODS) Core package provides the foundation for modeling complex domains with Domain-Driven Design (DDD) principles. It serves two main purposes:
-
Schema Types β ODS Core defines the types that make up the JSON Schema specification itself. These types form the authoritative, open standard for describing domains.
-
TypeScript Toolkit β Beyond the specification, ODS Core offers a set of TypeScript wrappers and classes to make it easier to construct domain models directly in code. These utilities provide a practical, developer-friendly way to create, validate, and share domain models as code.
The advantage of this open approach is that once defined, schemas can be applied across many downstream use cases, such as:
- πΊοΈ Domain Visualization: Tools that visualize domain models as diagrams or graphs
- ποΈ Scaffolding: Generating code templates based on domain models
- π Documentation: Automatically generating documentation from domain models
- β¨ GenAI: Using domain models to inform AI systems about the high level business intent and how the implementation should align with it
Domain Hierarchyβ
The ODS core follows a hierarchical structure aligned with Domain-Driven Design principles:
Workspace
βββ Domain (core/supporting/generic)
β βββ Subdomain
β βββ Bounded Context
β βββ Service (application/domain/infrastructure)
β β βββ Consumables
β β βββ Consumptions
β βββ Aggregate
β βββ Entities
β βββ Value Objects
β βββ Invariants
β βββ Consumables
β βββ Consumptions
Installationβ
npm install @open-domain-specification/core
Building a Domain Modelβ
Using the Typescript API you can build domains using Typescript so they can be maintained as code.
Finally, once the domain model is defined, it can be exported as a JSON document and used for visualization, documentation, or other purposes.
import { Workspace } from "@open-domain-specification/core";
import { describe, expect, it } from "vitest";
const ws = new Workspace("eCommerce", {
odsVersion: "1.0.0",
description: "DDD workspace for an eCommerce platform example",
version: "0.1.0",
homepage: "https://example.com",
primaryColor: "#0ea5e9",
logoUrl: "https://example.com/logo.svg",
});
// === DOMAINS ===
const commerce = ws.addDomain("Commerce", {
description: "Core commerce capabilities",
type: "core",
});
const content = ws.addDomain("Content", {
description: "Supporting site content",
type: "supporting",
});
// === SUBDOMAINS ===
const sales = commerce.addSubdomain("Sales", {
description: "From cart to order",
});
const publishing = content.addSubdomain("Publishing", {
description: "Pages and articles",
});
// === BOUNDED CONTEXTS ===
const orderingBC = sales.addBoundedcontext("Ordering", {
description: "Checkout flow and orders",
});
const cmsBC = publishing.addBoundedcontext("CMS", {
description: "Site content retrieval/rendering",
});
// === ORDERING ===
const checkoutSvc = orderingBC.addService("CheckoutApp", {
description: "Application service for placing orders",
type: "application",
});
const orderAgg = orderingBC.addAggregate("Order", {
description: "Immutable record of a purchase",
});
const orderEntity = orderAgg.addRootEntity("Order", {
description: "Order header",
});
const moneyVO = orderAgg.addValueObject("Money", {
description: "Amount + currency",
});
orderEntity.uses(moneyVO, "totals");
orderAgg.addInvariant("TotalsNonNegative", {
description: "Order totals must be >= 0",
});
// Checkout exposes an operation to place orders
const placeOrderOp = checkoutSvc.provides("PlaceOrder", {
description: "Create an order from a valid checkout",
type: "operation",
pattern: "open-host-service",
});
// === CMS ===
const rendererSvc = cmsBC.addService("ContentRenderer", {
description: "Application service for fetching/rendering content",
type: "application",
});
const articleAgg = cmsBC.addAggregate("Article", {
description: "Renderable content unit",
});
const articleEntity = articleAgg.addRootEntity("Article", {
description: "Article/page content",
});
const slugVO = articleAgg.addValueObject("Slug", {
description: "URL-safe identifier",
});
articleEntity.uses(slugVO, "addressable by");
articleAgg.addInvariant("SlugFormatValid", {
description: "Slug must be lowercase, dash-separated",
});
// CMS exposes an operation to get content
const getArticleOp = rendererSvc.provides("GetArticle", {
description: "Fetch article/page content by slug",
type: "operation",
pattern: "open-host-service",
});
// === CONSUMES ===
// Ordering needs content (e.g., terms page) during checkout
checkoutSvc.consumes(getArticleOp, {
pattern: "anti-corruption-layer",
});
// CMS might reference latest orders count/snippet for a dynamic page
rendererSvc.consumes(placeOrderOp, {
pattern: "conformist",
});
describe("Visitor Example", () => {
it("should compile ", () => {
expect(ws.toSchema()).toMatchSnapshot();
});
});