Command Model

Command hierarchy

Diagram

The Command interface is a Java sealed interface with three permitted subtypes:

public sealed interface Command
    permits TenantCommand, UserCommand, GlobalCommand

Every command carries two metadata fields:

  • getCmdUuid() — unique identifier, generated automatically by the tracing interceptor

  • getCmdSourceRef() — optional reference to the source that triggered the command. Often set to the cmdUuid of another command, creating a chain that lets you trace how one command propagates changes through the system.

TenantCommand

For operations scoped to a tenant. Extends Command and TenantIdAware.

@Data
@Introspected
public class CreateOrderCommand implements TenantCommand {
    private UUID cmdUuid;
    private String cmdSourceRef;
    private String tenantId;
    // command-specific fields
}

UserCommand

For operations scoped to a user (without tenant context). Extends Command and UserIdAware.

GlobalCommand

For operations that are not scoped to a tenant or user. Extends Command directly.

Combining interfaces

The sealed subtypes define the primary scope of a command, but marker interfaces can be freely combined. This is composition over inheritance — pick a base type and mix in what you need:

@Data
@Introspected
public class GenerateTokenCommand implements TenantCommand, UserIdAware {
    private UUID cmdUuid;
    private String cmdSourceRef;
    private String tenantId;
    private String userId;
    // tenant-scoped, but also tracks the user
}
@Data
@Introspected
public class CreateReportCommand implements TenantCommand, DryRunEnabled, AttributesEnabled {
    private UUID cmdUuid;
    private String cmdSourceRef;
    private String tenantId;
    private boolean dryRun;
    // tenant-scoped, supports dry-run, carries custom attributes

    @Override
    public Map<String, Object> attributes() {
        return Map.of("reportType", "annual", "year", 2025);
    }
}

The tracing framework inspects these interfaces at runtime:

  • TenantIdAware → populates tenant_id in the command log

  • UserIdAware → populates user_id

  • DryRunEnabled → sets the dry_run flag

  • AttributesEnabled → stores the attributes map as JSONB

Marker interfaces

Commands can implement additional interfaces to enable framework behaviors.

DryRunEnabled

Commands that support a dry-run mode. When dryRun() returns true, the tracing framework records the command with the dry_run flag set.

public class CreateReportCommand implements TenantCommand, DryRunEnabled {
    // ...
    private boolean dryRun;

    @Override
    public boolean dryRun() {
        return dryRun;
    }
}

AttributesEnabled

Commands that carry custom key-value attributes. The attributes() map is stored in the attributes JSONB column of the command log.

public class ProcessPaymentCommand implements TenantCommand, AttributesEnabled {
    // ...
    @Override
    public Map<String, Object> attributes() {
        return Map.of("paymentMethod", "invoice", "currency", "NOK");
    }
}

ClientRefAware

For commands that carry a client-supplied reference identifying the originating client of the command chain (for example a machine, browser-id, app, or panel-id).

Unlike getCmdSourceRef(), which is replaced when a command spawns a new command, clientRef propagates unchanged through the chain so that events triggered by the originator can be recognised by the same client.

public class CreateOrderCommand implements TenantCommand, ClientRefAware {
    private UUID cmdUuid;
    private String cmdSourceRef;
    private String tenantId;
    private String clientRef;
    // ...
}

getClientRef() may return null when the client did not supply a reference. The tracing framework propagates a non-null clientRef from the root command to spawned commands automatically.

Redactable

For commands carrying a payload that must not land in the audit log row — typically file uploads, raw imports, or fields covered by data-handling rules. The framework calls redactedForTracing() just before cmd_body is built and uses what it returns as the body source. Identity (cmd uuid, tenant/user/client refs) is preserved automatically — Redactable only governs the body.

public record UploadInvoicePdfCommand(
    UUID cmdUuid,
    String cmdSourceRef,
    String tenantId,
    byte[] pdfBytes
) implements TenantCommand, Redactable {

    @Override
    public Optional<Command> redactedForTracing() {
        return Optional.of(new UploadInvoicePdfCommand(cmdUuid, cmdSourceRef, tenantId, null));
    }
}

The return shapes:

  • Optional.empty() — fully omit the body (cmd_body ends up as the empty-map sentinel).

  • Optional.of(copy) — use copy as the body source.

The implementation must return a new instance, never mutate this — the original cmd is still in flight when redactedForTracing() is called.

If the implementation throws, the framework falls back to publishing the un-redacted cmd and emits a warn on logger no.conta.command.tracing.Redaction starting with "Redactable.redactedForTracing threw" — alert on it.

See Sensitive data for guidance on choosing between Redactable and ExcludeCmdBody.

TenantIdAware / UserIdAware

Implemented automatically by TenantCommand and UserCommand respectively. Can also be combined — a command can implement both TenantCommand and UserIdAware to carry both tenant and user context:

public class GenerateTokenCommand implements TenantCommand, UserIdAware {
    private UUID cmdUuid;
    private String cmdSourceRef;
    private String tenantId;
    private String userId;
    // ...
}

PatchAware

For commands that carry partial updates (PATCH semantics). Provides helpers to check which fields are present in the patch and retrieve their values.