Guides

One core or several

Most apps need one core. When a second one arrives, a vendor's SDK or a module another team ships, the two load into the same app side by side, each under its own namespace.

One core first#

Put the app's domain in one core and split it into crates inside it. Add a second core only for code that ships on its own.

One core, several cratesSeveral cores
State and transactionsshared: one store graph, one order of writesseparate: each core has its own
Releasestogethereach on its own schedule
Costone runtimea runtime, its threads and a copy of Undra per core

The namespace#

A core's namespace names everything it exports, so two cores never collide. By default it is the core's package name in snake case; set it in undra.toml (lowercase letters, digits and _, at most 32 characters).

undra.toml
[core]
path = "core"
namespace = "acme_pay"
WhatNamed
The C symbol, which returns the core's tableacme_pay_undra_api
iOSAcmePayCore.xcframework, one prelinked libacme_pay.a per slice
Androidlibacme_pay.so
Webacme_pay.wasm
The bindings' entry, in Swift, Kotlin and TypeScriptUndraAcmePay

undra build refuses a namespace that a sibling project already uses, and every runtime refuses to load two cores with one.

The namespace also keeps the default storage apart: each core's Kv, SecureStore, Fs and Db live under undra/<namespace>/ on every platform, so two cores that use the defaults never see each other's data unless you hand them one adapter of your own.

Loading two cores#

Load each core once, through its own entry. Every generated type defaults to the core of its own package, so a call never reaches the wrong one.

Show the code for Swift, Kotlin and TypeScript
swift
try UndraShopCore.load()
try UndraAcmePay.load()

let cart = try Cart()            // Shop's core
let quote = try Quote()          // AcmePay's core
kotlin
val shop = UndraShopCore.load()
UndraAcmePay.load()
AndroidPlatformDefaults.install(shop, this)   // the ports, per core
ts
await UndraShopCore.load({ mode: "wasm-main", wasm: new URL(shopWasm, location.href) });
await UndraAcmePay.load({ mode: "wasm-main", wasm: new URL(payWasm, location.href), shared: false });

On iOS each slice exports one symbol, so the two libraries link next to each other with no -force_load. On Android each core registers its natives on its bindings' own class. In React Native, loadNative(UndraAcmePay) installs one module per namespace.

Good to know#

The two-core test apps load one core under two namespaces on iOS, Android, the JVM and Node, and CI runs them on every change.

Edit this page on GitHub