test(event-subscriber): the projection is sourced from register records (refs #153)
Ports, schema and failing tests for the subscriber half of S-19b-2, ahead of the implementation. The subscriber now listens on the `objecten` kanaal instead of `zaken`. An Objecten notification carries no record data — only the object URL — so the record is read back through the ACL (§8.1), and the zaak-shaped surface goes away: IsZaakCreated / IsZaakStatusSet / ZaakUrl / ZaakId and ToEntry's `Resource == "status"` mapping are replaced by IsRegisterRecordWritten + ObjectUrl. The notification log now holds the projected row itself (register id, status, reference), so a rebuild is a replay with no mapping rules and no upstream reads. The migration drops the old columns rather than renaming them — EF scaffolded renames that would have carried ZGW values into columns meaning something else — and empties both tables, since a pre-slice row is neither reprojectable nor re-derivable from the new source. Red: HandleAsync recognises a register write but does not yet read or project it, so the seven projection assertions fail on an empty store.
This commit is contained in:
+87
@@ -0,0 +1,87 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using Projection.ReadModel;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Projection.ReadModel.Migrations
|
||||
{
|
||||
[DbContext(typeof(ProjectionDbContext))]
|
||||
[Migration("20260828103132_ProjectionSourcedFromObjecten")]
|
||||
partial class ProjectionSourcedFromObjecten
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Projection.ReadModel.ProcessedNotificationRow", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("key");
|
||||
|
||||
b.Property<DateTimeOffset>("ReceivedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("received_at");
|
||||
|
||||
b.Property<string>("Reference")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("reference");
|
||||
|
||||
b.Property<string>("RegisterId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("register_id");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("processed_notifications", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Projection.ReadModel.RegisterEntryRow", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<string>("Bsn")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("bsn");
|
||||
|
||||
b.Property<string>("NaamPlaceholder")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("naam_placeholder");
|
||||
|
||||
b.Property<string>("Reference")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("reference");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("register_projection", (string)null);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Projection.ReadModel.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// S-19b-2 (ADR-0030): the notification log stops describing ZGW zaak events and starts holding
|
||||
/// the projected register row itself (register id, status, reference).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The old columns are dropped and the new ones added rather than renamed. EF scaffolded renames
|
||||
/// (<c>resource</c> → <c>register_id</c>, <c>zaak_id</c> → <c>status</c>), which would carry ZGW
|
||||
/// values into columns that mean something else entirely — "zaak"/"status" as a register id, a
|
||||
/// zaak uuid as a register status — and a rebuild would then project that garbage.
|
||||
///
|
||||
/// Both tables are emptied instead. A pre-existing row describes a zaak event the new projector
|
||||
/// cannot reproject, and the registrations behind those rows have no RegisterRecord in Objecten
|
||||
/// (only approvals wrote one before this slice), so they are not re-derivable from the new source
|
||||
/// either. The projection is a derived artefact (§8.4) and repopulates as register writes arrive.
|
||||
/// </remarks>
|
||||
public partial class ProjectionSourcedFromObjecten : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// ponytail: drops the pre-slice register rather than backfilling it. Fine while stacks are
|
||||
// ephemeral (a fresh `docker compose up` is the norm). If a long-lived environment ever
|
||||
// needs to keep them, backfill by walking Objecten's objects instead of replaying the log.
|
||||
migrationBuilder.Sql("DELETE FROM processed_notifications;");
|
||||
migrationBuilder.Sql("DELETE FROM register_projection;");
|
||||
|
||||
migrationBuilder.DropColumn(name: "actie", table: "processed_notifications");
|
||||
migrationBuilder.DropColumn(name: "zaak_id", table: "processed_notifications");
|
||||
migrationBuilder.DropColumn(name: "resource", table: "processed_notifications");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "register_id",
|
||||
table: "processed_notifications",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "status",
|
||||
table: "processed_notifications",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql("DELETE FROM processed_notifications;");
|
||||
migrationBuilder.Sql("DELETE FROM register_projection;");
|
||||
|
||||
migrationBuilder.DropColumn(name: "register_id", table: "processed_notifications");
|
||||
migrationBuilder.DropColumn(name: "status", table: "processed_notifications");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "actie", table: "processed_notifications", type: "text", nullable: false, defaultValue: "");
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "zaak_id", table: "processed_notifications", type: "text", nullable: false, defaultValue: "");
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "resource", table: "processed_notifications", type: "text", nullable: false, defaultValue: "");
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-9
@@ -28,11 +28,6 @@ namespace Projection.ReadModel.Migrations
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("key");
|
||||
|
||||
b.Property<string>("Actie")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("actie");
|
||||
|
||||
b.Property<DateTimeOffset>("ReceivedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("received_at");
|
||||
@@ -41,15 +36,15 @@ namespace Projection.ReadModel.Migrations
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("reference");
|
||||
|
||||
b.Property<string>("Resource")
|
||||
b.Property<string>("RegisterId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("resource");
|
||||
.HasColumnName("register_id");
|
||||
|
||||
b.Property<string>("ZaakId")
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("zaak_id");
|
||||
.HasColumnName("status");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user