Skip to content

Standalone Quickstart

This guide is for services that wire DSM directly in Java code without Spring Boot auto-configuration.

Use this path when your service already owns:

  • its own bootstrap sequence
  • membership implementation selection
  • runtime lifecycle control
  • collection registration in application code

Integration Checklist

Before writing code, decide these four inputs:

  1. what clusterId and serviceId will be in production
  2. which membership implementation the service will use
  3. which collections will be registered during bootstrap
  4. whether registration is fixed at startup or dynamic later

1. Add The Core Module

xml
<dependency>
  <groupId>com.leanowtech.dsm</groupId>
  <artifactId>dsm-core</artifactId>
  <version>${dsm.version}</version>
</dependency>

2. Create The Membership Component

For local development or tests, StandaloneClusterMembership is the shortest path.

java
NodeInfo self = new NodeInfo("node-a", "127.0.0.1", 9090);

ClusterMembership membership = new StandaloneClusterMembership(self);

In production you will usually use multicast or unicast gossip membership from dsm-cluster, but the runtime API stays the same.

3. Build The Runtime

java
DsmRuntime runtime = DsmRuntimeBuilder.builder()
    .clusterId("runtime-example")
    .serviceId("gateway-service")
    .membership(membership)
    .allowDynamicRegistration(false)
    .build();

DsmRuntimeBuilder also allows advanced customization such as a custom collection registry, custom collection store, and custom clock, but most integrations should start with defaults.

4. Register Collections During Bootstrap

Register collection handles before start() when the service has a fixed collection set.

Register Collection

java
DsmRegister<RouteHint> routeHints = runtime.register(
    CollectionSpecBuilder.<RouteHint>register(
            "shared",
            "gateway",
            "route-hints")
        .schemaId("route-hints/v1")
        .codec(new RouteHintCodec())
        .build());

Lease Collection

java
DsmLeaseRegister<ShardOwner> shardOwners = runtime.leaseRegister(
    LeaseCollectionSpecBuilder.<ShardOwner>lease(
            "shared",
            "worker",
            "shard-owner")
        .schemaId("shard-owner/v1")
        .codec(new ShardOwnerCodec())
        .entityFactory(ShardOwner::blank)
        .build());

CRDT Collection

java
DsmCrdtCollection<PnCounterUpdate, PnCounterState> counters = runtime.crdt(
    CrdtCollectionSpecBuilder.<PnCounterUpdate, PnCounterState>crdt(
            "shared",
            "billing",
            "request-counter")
        .schemaId("request-counter/v1")
        .codec(new PnCounterUpdateCodec())
        .stateCodec(new PnCounterStateCodec())
        .initialState(PnCounterState.empty())
        .build(),
    new PnCounterStateMerger());

5. Start The Runtime

java
runtime.start();

The runtime is not serving local work until lifecycle services are started.

6. Expose The Handles Through Your Service Boundary

Most services should not let every layer call runtime.register(...) directly. Prefer a thin integration layer that owns DSM handles and exposes domain operations.

java
public final class RouteHintRepository {

    private final DsmRegister<RouteHint> routeHints;

    public RouteHintRepository(DsmRegister<RouteHint> routeHints) {
        this.routeHints = routeHints;
    }

    public void upsert(String key, String address) {
        routeHints.put(new RouteHint(key, null, address));
    }

    public Optional<RouteHint> find(String key) {
        return routeHints.get(key);
    }
}

This keeps collection locators and DSM-specific semantics out of unrelated application layers.

7. Use Diagnostics And Lifecycle State

These APIs are the first place to look when a node appears live but DSM behavior is wrong.

java
RuntimeDiagnostics diagnostics = runtime.diagnostics();

System.out.println(runtime.state());
System.out.println(runtime.isReady());
System.out.println(runtime.info());
System.out.println(runtime.clusterView());
System.out.println(diagnostics.totalEntries());

Fixed Registration

Use bootstrap registration when every node should expose the same collection set from startup. This is the safest default.

Dynamic Registration

Set allowDynamicRegistration(true) only if your service actually needs to add collections after runtime construction. Dynamic registration changes operational expectations and should not be enabled casually.

Tenant/Application Scoping

If you have multiple tenants or application domains, use runtime.tenant(...).application(...) to organize registration code. That keeps locator assembly consistent.

java
ApplicationRuntime gateway = runtime.tenant("shared").application("gateway");

DsmRegister<RouteHint> routeHints = gateway.register(
    CollectionSpecBuilder.<RouteHint>register(
            "shared",
            "gateway",
            "route-hints")
        .schemaId("route-hints/v1")
        .codec(new RouteHintCodec())
        .build());

Common Standalone Mistakes

Starting The Service Before Calling runtime.start()

Collection handles may exist, but lifecycle-dependent behavior is not fully initialized until the runtime starts.

Hiding Membership Choice Too Late

Membership affects how the runtime participates in the cluster. Decide the membership implementation as part of service bootstrap design, not as a late operational detail.

Mixing Domain Identity With Locator Identity

Your entity keys are domain keys. The collection locator is infrastructure identity. Both must be designed explicitly.