001-platform-events.xml 2.79 KB
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
                                       https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.27.xsd">

    <!--
        platform__event_outbox

        The transactional outbox table for the vibe_erp event bus.

        Why an outbox at all (architecture spec section 9, "Cross-cutting"):
        the bus must guarantee "publish-and-rollback can never escape" —
        i.e. an event published inside a transaction is delivered if and
        only if the surrounding transaction commits. The way to do that
        without two-phase commit between the DB and the message broker is
        to write the event row to the SAME database in the SAME transaction
        as the originating change. A separate poller picks up DISPATCHED
        rows and forwards them to in-process subscribers (today) or to
        Kafka/NATS (later) — and a publish that gets rolled back simply
        leaves no row to dispatch.

        Status machine:
          PENDING    — written by publish(), not yet picked up
          DISPATCHED — successfully delivered to all subscribers
          FAILED     — delivery failed; will retry until attempts >= max
    -->
    <changeSet id="platform-events-001" author="vibe_erp">
        <comment>Create platform__event_outbox table</comment>
        <sql>
            CREATE TABLE platform__event_outbox (
                id              uuid PRIMARY KEY,
                event_id        uuid         NOT NULL,
                topic           varchar(256) NOT NULL,
                aggregate_type  varchar(128) NOT NULL,
                aggregate_id    varchar(256) NOT NULL,
                payload         jsonb        NOT NULL,
                status          varchar(16)  NOT NULL DEFAULT 'PENDING',
                attempts        integer      NOT NULL DEFAULT 0,
                last_error      text,
                occurred_at     timestamptz  NOT NULL,
                created_at      timestamptz  NOT NULL DEFAULT now(),
                dispatched_at   timestamptz,
                version         bigint       NOT NULL DEFAULT 0
            );
            CREATE UNIQUE INDEX platform__event_outbox_event_id_uk
                ON platform__event_outbox (event_id);
            CREATE INDEX platform__event_outbox_status_created_idx
                ON platform__event_outbox (status, created_at);
            CREATE INDEX platform__event_outbox_topic_idx
                ON platform__event_outbox (topic);
        </sql>
        <rollback>
            DROP TABLE platform__event_outbox;
        </rollback>
    </changeSet>

</databaseChangeLog>