From 74979e322602b5d6eb28fc473c3bce45baf1c4fc Mon Sep 17 00:00:00 2001 From: zgw <13623675548@163.com> Date: Mon, 10 Aug 2026 15:05:57 +0800 Subject: [PATCH] feat(wcs): add shared domain contracts --- .../ruoyi/wcs/domain/shared/DomainEvent.java | 51 +++++++++++++++++++ .../domain/shared/DomainRuleViolation.java | 7 +++ .../ruoyi/wcs/domain/shared/IdGenerator.java | 5 ++ .../com/ruoyi/wcs/domain/shared/WcsClock.java | 7 +++ .../domain/shared/SharedContractsTest.java | 36 +++++++++++++ 5 files changed, 106 insertions(+) create mode 100644 ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/DomainEvent.java create mode 100644 ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/DomainRuleViolation.java create mode 100644 ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/IdGenerator.java create mode 100644 ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/WcsClock.java create mode 100644 ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/shared/SharedContractsTest.java diff --git a/ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/DomainEvent.java b/ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/DomainEvent.java new file mode 100644 index 0000000..77a7898 --- /dev/null +++ b/ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/DomainEvent.java @@ -0,0 +1,51 @@ +package com.ruoyi.wcs.domain.shared; + +import java.time.Instant; +import java.util.Objects; + +public final class DomainEvent { + private final String eventId; + private final String aggregateId; + private final long sequence; + private final Instant occurredAt; + private final String type; + + public DomainEvent(String eventId, String aggregateId, long sequence, + Instant occurredAt, String type) { + this.eventId = requireText(eventId, "eventId"); + this.aggregateId = requireText(aggregateId, "aggregateId"); + if (sequence < 1L) { + throw new IllegalArgumentException("sequence must be positive"); + } + this.sequence = sequence; + this.occurredAt = Objects.requireNonNull(occurredAt, "occurredAt"); + this.type = requireText(type, "type"); + } + + public String getEventId() { + return eventId; + } + + public String getAggregateId() { + return aggregateId; + } + + public long getSequence() { + return sequence; + } + + public Instant getOccurredAt() { + return occurredAt; + } + + public String getType() { + return type; + } + + private static String requireText(String value, String field) { + if (value == null || value.trim().isEmpty()) { + throw new IllegalArgumentException(field + " must not be blank"); + } + return value; + } +} diff --git a/ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/DomainRuleViolation.java b/ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/DomainRuleViolation.java new file mode 100644 index 0000000..03c6059 --- /dev/null +++ b/ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/DomainRuleViolation.java @@ -0,0 +1,7 @@ +package com.ruoyi.wcs.domain.shared; + +public final class DomainRuleViolation extends RuntimeException { + public DomainRuleViolation(String message) { + super(message); + } +} diff --git a/ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/IdGenerator.java b/ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/IdGenerator.java new file mode 100644 index 0000000..7d9a8ae --- /dev/null +++ b/ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/IdGenerator.java @@ -0,0 +1,5 @@ +package com.ruoyi.wcs.domain.shared; + +public interface IdGenerator { + String nextId(); +} diff --git a/ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/WcsClock.java b/ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/WcsClock.java new file mode 100644 index 0000000..699d8b6 --- /dev/null +++ b/ruoyi-wcs/src/main/java/com/ruoyi/wcs/domain/shared/WcsClock.java @@ -0,0 +1,7 @@ +package com.ruoyi.wcs.domain.shared; + +import java.time.Instant; + +public interface WcsClock { + Instant now(); +} diff --git a/ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/shared/SharedContractsTest.java b/ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/shared/SharedContractsTest.java new file mode 100644 index 0000000..d1efabb --- /dev/null +++ b/ruoyi-wcs/src/test/java/com/ruoyi/wcs/domain/shared/SharedContractsTest.java @@ -0,0 +1,36 @@ +package com.ruoyi.wcs.domain.shared; + +import org.junit.jupiter.api.Test; + +import java.time.Instant; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class SharedContractsTest { + + @Test + void rejectsBlankEventIdentity() { + assertThrows(IllegalArgumentException.class, () -> + new DomainEvent("", "ORDER-1", 1L, Instant.EPOCH, "OrderAccepted")); + } + + @Test + void rejectsInvalidEventSequence() { + assertThrows(IllegalArgumentException.class, () -> + new DomainEvent("E-1", "ORDER-1", 0L, Instant.EPOCH, "OrderAccepted")); + } + + @Test + void exposesDeterministicEventFields() { + Instant occurredAt = Instant.parse("2026-08-04T00:00:00Z"); + DomainEvent event = new DomainEvent("E-1", "ORDER-1", 2L, + occurredAt, "OrderPlanned"); + + assertEquals("E-1", event.getEventId()); + assertEquals("ORDER-1", event.getAggregateId()); + assertEquals(2L, event.getSequence()); + assertEquals(occurredAt, event.getOccurredAt()); + assertEquals("OrderPlanned", event.getType()); + } +}