| name | blong-realm |
| description | Create business domain boundaries in Blong framework. Realms separate business logic into independent, modular units that can be deployed as monolith or microservices. Make sure to use this skill whenever creating a new business domain or service in Blong — even if the user says 'add a new module', 'create a new service', or 'set up a new package'. |
Implementing a Realm
Overview
A realm is a business domain boundary in the Blong framework. Realms separate business logic into
independent, modular units that can be developed independently and deployed together (monolith) or
separately (microservices).
Key Pattern: Well-known layer folders (error, adapter, orchestrator, gateway, sim,
test) are auto-discovered — no layer.server.ts needed. Custom folder names can add a
layer.server.ts / layer.browser.ts to declare activation.
Purpose
- Modular Development: Focus on specific business functionality
- Team Independence: Teams can develop realms end-to-end
- Deployment Flexibility: Same code can run as monolith or microservices
- Clear Boundaries: Avoid coupling between different business domains
File Structure
realmname/
├── server.ts # Optional — only for realm-level config/validation
├── package.json # Package definition (if separate package)
├── adapter/ # Auto-discovered (well-known name)
│ └── db.ts # Self-contained adapter
├── orchestrator/ # Auto-discovered (well-known name)
│ └── dispatch.ts # Self-contained orchestrator
├── gateway/ # Auto-discovered (well-known name)
├── error/ # Auto-discovered (well-known name)
└── test/ # Auto-discovered (well-known name)
Custom layer folders need a layer.server.ts:
realmname/
└── myCustomLayer/
└── layer.server.ts # Required: declares activation for non-well-known folder
layer.server.ts / layer.browser.ts
Only needed for non-well-known folder names. Declares activation per environment.
import {layer} from '@feasibleone/blong';
export default layer({
default: true,
microservice: true,
});
import {layer} from '@feasibleone/blong';
export default layer({
integration: true,
});
Well-known Folder Defaults
Well-known folders are automatically activated without any layer.*.ts file. The key in each cell
is the intent that must be active for the layer to load:
| Folder | Server intent | Browser intent |
|---|
error | default (always) | — |
adapter | default (always) | default (always) |
orchestrator | default (always) | — |
gateway | default (always) | — |
sim | integration | — |
test | integration | integration |
Override any default by adding a layer.server.ts / layer.browser.ts to the folder. See the
blong-intent skill for the full intents reference and how to create custom intents.
Minimal server.ts (Only When Needed)
server.ts is only needed when the realm has:
- Realm-level validation schema
- Realm-level default config (e.g. keys, URLs shared across layers)
import {realm} from '@feasibleone/blong';
export default realm(blong => ({
url: import.meta.url,
validation: blong.type.Object({
myService: blong.type.Object({
url: blong.type.String(),
}),
}),
config: {
default: {
myService: {
url: 'http://localhost:8080',
},
},
},
}));
Layer Files Define Their Own Config
Each adapter/orchestrator defines its own configuration:
import {adapter} from '@feasibleone/blong';
export default adapter(blong => ({
extends: 'adapter.knex',
validation: blong.type.Object({
namespace: blong.type.Union([blong.type.String(), blong.type.Array(blong.type.String())]),
imports: blong.type.Union([blong.type.String(), blong.type.Array(blong.type.String())]),
}),
activation: {
default: {
namespace: 'db/$subject',
imports: '$subject.db',
},
},
}));
import {orchestrator} from '@feasibleone/blong';
export default orchestrator(blong => ({
extends: 'orchestrator.dispatch',
activation: {
default: {
destination: 'db',
namespace: ['$subject'],
imports: [/^$subject\./],
validations: [/^$subject\.\w+\.validation$/],
},
},
}));
Configuration Concepts
Environment Activations
default: Base configuration active for all cases
dev: Development environment overrides
prod: Production/UAT environment overrides
test: Automated testing activation
db: Database creation/migration mode
realm: Single realm focus for development
microservice: Production microservice deployment
integration: Integration testing mode
Layer Activation
Set layer names to true to activate them in server.ts:
config: {
test: {
error: true,
adapter: true,
orchestrator: true,
gateway: true,
test: true
}
}
Config Priority (highest to lowest)
- CLI parameters (
--config.db.host=localhost)
- Environment variables (
BLONG_DB_HOST)
- Environment-specific layer config (layer's
config.dev)
- Layer's default config (layer's
config.default)
- Framework defaults
Loading Children
Children can be loaded as:
Local Paths (with auto-discovery)
children: [
'./adapter',
'./orchestrator',
];
The framework auto-discovers .ts files in each child folder. If a server.ts exists in the child
folder, it is used as the realm entry point instead (for nested realms like sub-domains).
Async Imports (for external packages)
When including external realm packages, use async imports in the APPLICATION-level server.ts /
browser.ts:
children: [
async () => import('@feasibleone/blong-login/server.js'),
async () => import('@feasibleone/blong-openapi/server.js'),
];
Best Practices
- Well-known folders are zero-config:
error, adapter, orchestrator, gateway, sim,
test are auto-discovered with sensible defaults — no layer.server.ts needed
- Use
layer.server.ts only for custom folders: Non-well-known layer names must declare
activation
- Omit
server.ts for standard realms — the framework auto-discovers well-known layer folders
- Name Consistency: Use the same name for realm folder, package name, and namespace prefix
- Co-located Config: Put adapter/orchestrator config inside the adapter/orchestrator file using
the
adapter(blong => ...) pattern
- Keep server.ts only when realm-level validation schema or shared default config is needed
Examples from Codebase
See core/test/demo/ for a complete example without server.ts:
- No
layer.server.ts files — auto-discovered via well-known folder names
- Each adapter/orchestrator file is self-contained with its own config
See core/blong-marine/ for an example of a realm that can run both standalone (own Vite /
Storybook / Playwright) and as a child of a larger suite:
browser.ts and server.ts are browser() / server() suite entries (not realm())
- They include all their own infrastructure (blong-browser, blong-server, blong-login) as children
- A parent suite (e.g.
core/blong-suite/) simply imports @feasibleone/blong-marine/browser.ts as
a child;
- Adding a second realm to the suite: append one line to the parent
browser.ts children array and
add the package to realmPackages in playwright.config.ts and .storybook/main.ts
Standalone Realm Entry Points
When a realm is developed and tested as a standalone package (with its own Vite, Storybook, and
Playwright setup), it needs additional entry point files that wire its infrastructure dependencies.
index.ts — server bootstrap for testing
index.ts exports a server() definition that wraps the realm with the dependencies it needs
(typically blong-server, blong-login, and the realm itself). The framework auto-detects the
server() kind and runs it via runPlatform() — no import from blong-gogo needed:
import {server} from '@feasibleone/blong';
import pkg from './package.json' with {type: 'json'};
export default server(() => ({
url: import.meta.url,
pkg: {name: pkg.name, version: pkg.version},
children: [
async function srv() {
return import('@feasibleone/blong-server/server.ts');
},
async function login() {
return import('@feasibleone/blong-login/server.ts');
},
async function realm() {
return import('./server.ts');
},
],
config: {
default: {},
dev: {srv: {}, realm: {}, login: {}},
},
}));
This is a dev dependency pattern — index.ts wires the realm's runtime infrastructure for local
development and integration tests. It is not imported by a parent suite; the parent suite imports
./server.ts instead.
index.html.ts — browser entry point (Vite)
index.html.ts is the TypeScript Vite entry point (loaded by index.html). It loads the browser
platform using blong-gogo:
import load from '@feasibleone/blong-gogo';
import browser from './index.browser.ts';
import pkg from './package.json' with {type: 'json'};
load(browser, pkg.name, {apiSchema: false}, ['microservice', 'integration', 'dev'])
.then(platform => platform.start({}))
.catch(console.error);
index.browser.ts — browser suite definition
index.browser.ts is the browser-platform equivalent of index.ts. It exports a browser()
definition that wires the realm's browser infrastructure (blong-browser, login, and the realm):
import {browser} from '@feasibleone/blong';
import pkg from './package.json' with {type: 'json'};
export default browser(blong => ({
url: import.meta.url,
pkg: {name: pkg.name, version: pkg.version},
validation: blong.type.Object({realm: blong.type.Object({})}),
children: [
async function ui() {
return import('@feasibleone/blong-browser/browser.ts');
},
async function realm() {
return import('./browser.ts');
},
],
config: {
default: {
ui: {portal: {portal: {title: 'My Realm'}}},
realm: {},
},
},
}));
index.html references ./index.html.ts as its module entry:
<script
type="module"
src="./index.html.ts"
></script>