eventlog.events
The durable event table written with each action transaction.
eventlog.events is the durable event table used by the default SingleTableJsonEventPersister. Domain rows and event rows are committed in the same database transaction, so this table becomes the reliable source that local handlers, CDC connectors, routers, and broker publishers consume after commit.
Required when: you use the default SingleTableJsonEventPersister. This is the normal Ekbatan setup.
Writer: ActionExecutor, through SingleTableJsonEventPersister.
Row model: io.ekbatan.core.action.persister.event.single_table_json.EventEntity.
EventEntity is an internal row representation, not a domain Model or Entity. It has no optimistic-lock version and no soft-delete state.
DDL
PostgreSQL can create the eventlog schema in the same migration that creates the table. MariaDB and MySQL create a separate eventlog database in V0000, because those engines treat schema and database as the same namespace.
1
CREATE SCHEMA IF NOT EXISTS eventlog;
2
3
CREATE TABLE eventlog.events (
4
id UUID PRIMARY KEY,
5
namespace VARCHAR(255) NOT NULL,
6
action_id UUID NOT NULL,
7
action_name VARCHAR(255) NOT NULL,
8
action_params JSONB NOT NULL,
9
started_date TIMESTAMP NOT NULL,
10
completion_date TIMESTAMP NOT NULL,
11
model_id VARCHAR(255),
12
model_type VARCHAR(255),
13
event_type VARCHAR(255),
14
payload JSONB,
15
event_date TIMESTAMP NOT NULL,
16
delivered BOOLEAN NOT NULL
17
);
18
19
CREATE INDEX idx_events_action_id ON eventlog.events(action_id);
The connecting user must have permission to create and access the eventlog database. The examples grant that permission from the container init script before Flyway runs.
1
CREATE DATABASE IF NOT EXISTS eventlog;
1
CREATE TABLE eventlog.events (
2
id UUID PRIMARY KEY,
3
namespace VARCHAR(255) NOT NULL,
4
action_id UUID NOT NULL,
5
action_name VARCHAR(255) NOT NULL,
6
action_params JSON NOT NULL,
7
started_date DATETIME(6) NOT NULL,
8
completion_date DATETIME(6) NOT NULL,
9
model_id VARCHAR(255),
10
model_type VARCHAR(255),
11
event_type VARCHAR(255),
12
payload JSON,
13
event_date DATETIME(6) NOT NULL,
14
delivered BOOLEAN NOT NULL
15
);
16
17
CREATE INDEX idx_events_action_id ON eventlog.events(action_id);
The connecting user must have permission to create and access the eventlog database. The examples grant that permission from the container init script before Flyway runs.
1
CREATE DATABASE IF NOT EXISTS eventlog;
1
CREATE TABLE eventlog.events (
2
id CHAR(36) CHARACTER SET ascii NOT NULL,
3
namespace VARCHAR(255) NOT NULL,
4
action_id CHAR(36) CHARACTER SET ascii NOT NULL,
5
action_name VARCHAR(255) NOT NULL,
6
action_params JSON NOT NULL,
7
started_date DATETIME(6) NOT NULL,
8
completion_date DATETIME(6) NOT NULL,
9
model_id VARCHAR(255),
10
model_type VARCHAR(255),
11
event_type VARCHAR(255),
12
payload JSON,
13
event_date DATETIME(6) NOT NULL,
14
delivered BOOLEAN NOT NULL,
15
PRIMARY KEY (id)
16
);
17
18
CREATE INDEX idx_events_action_id ON eventlog.events(action_id);
Columns
| Column | Nullable | Meaning |
|---|---|---|
id | No | Primary key of this event row. For cross-shard actions, each shard receives rows with the same event ids so every shard has a self-contained action/event picture. |
namespace | No | Logical producer namespace configured on ActionExecutor. Use it to distinguish services or bounded contexts when several applications emit events. |
action_id | No | One UUID per action execution. If one action emits multiple events, all rows from that action share the same action_id. |
action_name | No | Simple class name of the producing action, such as WalletDepositMoneyAction. |
action_params | No | The action params object serialized as JSON. This lets consumers understand the command that produced the event without joining to application tables. |
started_date | No | UTC instant when the action’s perform() phase began. |
completion_date | No | UTC instant when the action’s persist phase completed. |
model_id | Yes | Identifier of the affected model. NULL only on sentinel rows for actions that emitted no model event. |
model_type | Yes | Simple class name of the affected model, such as Wallet. NULL only on sentinel rows. |
event_type | Yes | Simple class name of the event class, such as WalletMoneyDepositedEvent. NULL on sentinel rows. The default persister rejects ambiguous event simple-name collisions inside one process. |
payload | Yes | Event payload serialized as JSON. NULL on sentinel rows. |
event_date | No | UTC instant when the event was logically produced, normally aligned with the action completion time. |
delivered | No | Starts as FALSE on every insert. The local-event-handler fan-out job flips it to TRUE after it materializes handler notifications. CDC-only deployments can ignore it. |
Sentinel rows
If an action emits zero events, Ekbatan still writes one sentinel row with model_id, model_type, event_type, and payload set to NULL. This preserves the fact that the action happened. Event consumers should skip sentinel rows by checking event_type IS NULL; Ekbatan’s Debezium SMTs already do that.
Indexes
| Index | Purpose |
|---|---|
idx_events_action_id on (action_id) | Lets tests, diagnostics, and consumers find all rows created by one action execution. |
The local event handler adds one extra fan-out index on this table. That index is documented with eventlog.event_notifications because it is only needed when local event handling is installed.
Dialect types
| Logical column group | PostgreSQL | MariaDB | MySQL |
|---|---|---|---|
id, action_id | UUID | UUID | CHAR(36) CHARACTER SET ascii |
action_params, payload | JSONB | JSON | JSON |
started_date, completion_date, event_date | TIMESTAMP | DATETIME(6) | DATETIME(6) |
| Text columns | VARCHAR(255) | VARCHAR(255) | VARCHAR(255) |
delivered | BOOLEAN | BOOLEAN | BOOLEAN |
See also
- Framework tables — table map and optionality matrix.
- The outbox: atomic state + events — why this table exists.
- Streaming via Debezium — reading committed event rows with CDC.
- Local event handler — fan-out from event rows into local handler notifications.