feat(wcs): add idempotent transport order use cases
This commit is contained in:
@@ -0,0 +1,76 @@
|
|||||||
|
package com.ruoyi.wcs.application.task;
|
||||||
|
|
||||||
|
import com.ruoyi.wcs.domain.task.TransportOrderType;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public final class CreateTransportOrderCommand {
|
||||||
|
private final String externalRequestId;
|
||||||
|
private final TransportOrderType orderType;
|
||||||
|
private final String loadId;
|
||||||
|
private final String sourceCode;
|
||||||
|
private final String destinationCode;
|
||||||
|
private final int priority;
|
||||||
|
private final Instant deadline;
|
||||||
|
private final Map<String, String> attributes;
|
||||||
|
|
||||||
|
public CreateTransportOrderCommand(String externalRequestId,
|
||||||
|
TransportOrderType orderType,
|
||||||
|
String loadId, String sourceCode,
|
||||||
|
String destinationCode, int priority,
|
||||||
|
Instant deadline,
|
||||||
|
Map<String, String> attributes) {
|
||||||
|
this.externalRequestId = requireText(externalRequestId, "externalRequestId");
|
||||||
|
this.orderType = Objects.requireNonNull(orderType, "orderType");
|
||||||
|
this.loadId = loadId;
|
||||||
|
this.sourceCode = requireText(sourceCode, "sourceCode");
|
||||||
|
this.destinationCode = requireText(destinationCode, "destinationCode");
|
||||||
|
this.priority = priority;
|
||||||
|
this.deadline = deadline;
|
||||||
|
this.attributes = Collections.unmodifiableMap(new LinkedHashMap<String, String>(
|
||||||
|
Objects.requireNonNull(attributes, "attributes")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExternalRequestId() {
|
||||||
|
return externalRequestId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransportOrderType getOrderType() {
|
||||||
|
return orderType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLoadId() {
|
||||||
|
return loadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSourceCode() {
|
||||||
|
return sourceCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDestinationCode() {
|
||||||
|
return destinationCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPriority() {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getDeadline() {
|
||||||
|
return deadline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getAttributes() {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String requireText(String value, String field) {
|
||||||
|
if (value == null || value.trim().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException(field + " must not be blank");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package com.ruoyi.wcs.application.task;
|
||||||
|
|
||||||
|
import com.ruoyi.wcs.domain.shared.DomainEvent;
|
||||||
|
import com.ruoyi.wcs.domain.shared.IdGenerator;
|
||||||
|
import com.ruoyi.wcs.domain.shared.WcsClock;
|
||||||
|
import com.ruoyi.wcs.domain.task.TransportOrder;
|
||||||
|
import com.ruoyi.wcs.domain.task.TransportOrderState;
|
||||||
|
import com.ruoyi.wcs.domain.task.WorkflowDefinition;
|
||||||
|
import com.ruoyi.wcs.domain.task.WorkflowInstance;
|
||||||
|
import com.ruoyi.wcs.port.in.TransportOrderUseCase;
|
||||||
|
import com.ruoyi.wcs.port.out.DomainEventPublisher;
|
||||||
|
import com.ruoyi.wcs.port.out.TransportOrderRepository;
|
||||||
|
import com.ruoyi.wcs.port.out.WorkflowDefinitionProvider;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public final class TransportOrderApplicationService implements TransportOrderUseCase {
|
||||||
|
private final TransportOrderRepository repository;
|
||||||
|
private final WorkflowDefinitionProvider workflowDefinitions;
|
||||||
|
private final DomainEventPublisher eventPublisher;
|
||||||
|
private final IdGenerator idGenerator;
|
||||||
|
private final WcsClock clock;
|
||||||
|
|
||||||
|
public TransportOrderApplicationService(TransportOrderRepository repository,
|
||||||
|
WorkflowDefinitionProvider workflowDefinitions,
|
||||||
|
DomainEventPublisher eventPublisher,
|
||||||
|
IdGenerator idGenerator,
|
||||||
|
WcsClock clock) {
|
||||||
|
this.repository = Objects.requireNonNull(repository, "repository");
|
||||||
|
this.workflowDefinitions = Objects.requireNonNull(
|
||||||
|
workflowDefinitions, "workflowDefinitions");
|
||||||
|
this.eventPublisher = Objects.requireNonNull(eventPublisher, "eventPublisher");
|
||||||
|
this.idGenerator = Objects.requireNonNull(idGenerator, "idGenerator");
|
||||||
|
this.clock = Objects.requireNonNull(clock, "clock");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public TransportOrderResult create(CreateTransportOrderCommand command) {
|
||||||
|
Objects.requireNonNull(command, "command");
|
||||||
|
TransportOrder existing = repository.findByExternalRequestId(
|
||||||
|
command.getExternalRequestId());
|
||||||
|
if (existing != null) {
|
||||||
|
return TransportOrderResult.from(existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
TransportOrder order = TransportOrder.accept(
|
||||||
|
idGenerator.nextId(),
|
||||||
|
command.getExternalRequestId(),
|
||||||
|
command.getOrderType(),
|
||||||
|
command.getLoadId(),
|
||||||
|
command.getSourceCode(),
|
||||||
|
command.getDestinationCode(),
|
||||||
|
command.getPriority(),
|
||||||
|
clock.now());
|
||||||
|
WorkflowDefinition definition = workflowDefinitions.definitionFor(order.getType());
|
||||||
|
WorkflowInstance workflow = WorkflowInstance.create(
|
||||||
|
idGenerator.nextId(), order.getOrderId(), definition);
|
||||||
|
order.plan(workflow.getWorkflowId());
|
||||||
|
|
||||||
|
repository.save(order, workflow);
|
||||||
|
publishAndClear(order);
|
||||||
|
return TransportOrderResult.from(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public TransportOrderResult cancel(String externalRequestId) {
|
||||||
|
TransportOrder order = requireByExternalRequestId(externalRequestId);
|
||||||
|
order.cancel();
|
||||||
|
repository.save(order);
|
||||||
|
publishAndClear(order);
|
||||||
|
return TransportOrderResult.from(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public TransportOrderResult suspend(String orderId, String reason) {
|
||||||
|
TransportOrder order = requireByOrderId(orderId);
|
||||||
|
order.suspend(reason);
|
||||||
|
repository.save(order);
|
||||||
|
publishAndClear(order);
|
||||||
|
return TransportOrderResult.from(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public TransportOrderResult resume(String orderId) {
|
||||||
|
TransportOrder order = requireByOrderId(orderId);
|
||||||
|
if (order.getWorkflowId() == null) {
|
||||||
|
throw new IllegalStateException("accepted order has no workflow to resume");
|
||||||
|
}
|
||||||
|
order.resumeToPlanned();
|
||||||
|
repository.save(order);
|
||||||
|
publishAndClear(order);
|
||||||
|
return TransportOrderResult.from(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void publishAndClear(TransportOrder order) {
|
||||||
|
List<DomainEvent> events = order.getUncommittedEvents();
|
||||||
|
eventPublisher.publish(events);
|
||||||
|
order.clearUncommittedEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TransportOrder requireByExternalRequestId(String externalRequestId) {
|
||||||
|
TransportOrder order = repository.findByExternalRequestId(
|
||||||
|
requireText(externalRequestId, "externalRequestId"));
|
||||||
|
if (order == null) {
|
||||||
|
throw new IllegalArgumentException("transport order not found");
|
||||||
|
}
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TransportOrder requireByOrderId(String orderId) {
|
||||||
|
TransportOrder order = repository.findByOrderId(requireText(orderId, "orderId"));
|
||||||
|
if (order == null) {
|
||||||
|
throw new IllegalArgumentException("transport order not found");
|
||||||
|
}
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String requireText(String value, String field) {
|
||||||
|
if (value == null || value.trim().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException(field + " must not be blank");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package com.ruoyi.wcs.application.task;
|
||||||
|
|
||||||
|
import com.ruoyi.wcs.domain.task.TransportOrder;
|
||||||
|
import com.ruoyi.wcs.domain.task.TransportOrderState;
|
||||||
|
import com.ruoyi.wcs.domain.task.TransportOrderType;
|
||||||
|
|
||||||
|
public final class TransportOrderResult {
|
||||||
|
private final String orderId;
|
||||||
|
private final String externalRequestId;
|
||||||
|
private final TransportOrderType orderType;
|
||||||
|
private final String sourceCode;
|
||||||
|
private final String destinationCode;
|
||||||
|
private final TransportOrderState state;
|
||||||
|
private final String workflowId;
|
||||||
|
|
||||||
|
private TransportOrderResult(TransportOrder order) {
|
||||||
|
this.orderId = order.getOrderId();
|
||||||
|
this.externalRequestId = order.getExternalRequestId();
|
||||||
|
this.orderType = order.getType();
|
||||||
|
this.sourceCode = order.getSourceCode();
|
||||||
|
this.destinationCode = order.getDestinationCode();
|
||||||
|
this.state = order.getState();
|
||||||
|
this.workflowId = order.getWorkflowId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TransportOrderResult from(TransportOrder order) {
|
||||||
|
return new TransportOrderResult(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderId() {
|
||||||
|
return orderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExternalRequestId() {
|
||||||
|
return externalRequestId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransportOrderType getOrderType() {
|
||||||
|
return orderType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSourceCode() {
|
||||||
|
return sourceCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDestinationCode() {
|
||||||
|
return destinationCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransportOrderState getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWorkflowId() {
|
||||||
|
return workflowId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.ruoyi.wcs.port.in;
|
||||||
|
|
||||||
|
import com.ruoyi.wcs.application.task.CreateTransportOrderCommand;
|
||||||
|
import com.ruoyi.wcs.application.task.TransportOrderResult;
|
||||||
|
|
||||||
|
public interface TransportOrderUseCase {
|
||||||
|
TransportOrderResult create(CreateTransportOrderCommand command);
|
||||||
|
|
||||||
|
TransportOrderResult cancel(String externalRequestId);
|
||||||
|
|
||||||
|
TransportOrderResult suspend(String orderId, String reason);
|
||||||
|
|
||||||
|
TransportOrderResult resume(String orderId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.ruoyi.wcs.port.out;
|
||||||
|
|
||||||
|
import com.ruoyi.wcs.domain.shared.DomainEvent;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface DomainEventPublisher {
|
||||||
|
void publish(List<DomainEvent> domainEvents);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.ruoyi.wcs.port.out;
|
||||||
|
|
||||||
|
import com.ruoyi.wcs.domain.task.TransportOrder;
|
||||||
|
import com.ruoyi.wcs.domain.task.WorkflowInstance;
|
||||||
|
|
||||||
|
public interface TransportOrderRepository {
|
||||||
|
TransportOrder findByExternalRequestId(String externalRequestId);
|
||||||
|
|
||||||
|
TransportOrder findByOrderId(String orderId);
|
||||||
|
|
||||||
|
void save(TransportOrder order, WorkflowInstance workflow);
|
||||||
|
|
||||||
|
void save(TransportOrder order);
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package com.ruoyi.wcs.port.out;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public final class WmsNotification {
|
||||||
|
private final String eventId;
|
||||||
|
private final String externalRequestId;
|
||||||
|
private final String orderId;
|
||||||
|
private final Instant occurredAt;
|
||||||
|
private final String eventType;
|
||||||
|
private final String resultCode;
|
||||||
|
private final String resultMessage;
|
||||||
|
private final Map<String, String> context;
|
||||||
|
|
||||||
|
public WmsNotification(String eventId, String externalRequestId,
|
||||||
|
String orderId, Instant occurredAt,
|
||||||
|
String eventType, String resultCode,
|
||||||
|
String resultMessage, Map<String, String> context) {
|
||||||
|
this.eventId = requireText(eventId, "eventId");
|
||||||
|
this.externalRequestId = requireText(externalRequestId, "externalRequestId");
|
||||||
|
this.orderId = requireText(orderId, "orderId");
|
||||||
|
this.occurredAt = Objects.requireNonNull(occurredAt, "occurredAt");
|
||||||
|
this.eventType = requireText(eventType, "eventType");
|
||||||
|
this.resultCode = resultCode;
|
||||||
|
this.resultMessage = resultMessage;
|
||||||
|
this.context = Collections.unmodifiableMap(new LinkedHashMap<String, String>(
|
||||||
|
Objects.requireNonNull(context, "context")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEventId() {
|
||||||
|
return eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExternalRequestId() {
|
||||||
|
return externalRequestId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderId() {
|
||||||
|
return orderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getOccurredAt() {
|
||||||
|
return occurredAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEventType() {
|
||||||
|
return eventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResultCode() {
|
||||||
|
return resultCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResultMessage() {
|
||||||
|
return resultMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String requireText(String value, String field) {
|
||||||
|
if (value == null || value.trim().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException(field + " must not be blank");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.ruoyi.wcs.port.out;
|
||||||
|
|
||||||
|
public interface WmsNotificationGateway {
|
||||||
|
WmsNotificationReceipt notify(WmsNotification notification);
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.ruoyi.wcs.port.out;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public final class WmsNotificationReceipt {
|
||||||
|
public enum Status {
|
||||||
|
DELIVERED,
|
||||||
|
RETRYABLE_FAILURE,
|
||||||
|
PERMANENT_FAILURE
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Status status;
|
||||||
|
private final String detail;
|
||||||
|
|
||||||
|
public WmsNotificationReceipt(Status status, String detail) {
|
||||||
|
this.status = Objects.requireNonNull(status, "status");
|
||||||
|
this.detail = detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDetail() {
|
||||||
|
return detail;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package com.ruoyi.wcs.application.task;
|
||||||
|
|
||||||
|
import com.ruoyi.wcs.domain.shared.DomainEvent;
|
||||||
|
import com.ruoyi.wcs.domain.shared.IdGenerator;
|
||||||
|
import com.ruoyi.wcs.domain.shared.WcsClock;
|
||||||
|
import com.ruoyi.wcs.domain.task.TaskStepType;
|
||||||
|
import com.ruoyi.wcs.domain.task.TransportOrder;
|
||||||
|
import com.ruoyi.wcs.domain.task.TransportOrderState;
|
||||||
|
import com.ruoyi.wcs.domain.task.TransportOrderType;
|
||||||
|
import com.ruoyi.wcs.domain.task.WorkflowDefinition;
|
||||||
|
import com.ruoyi.wcs.domain.task.WorkflowInstance;
|
||||||
|
import com.ruoyi.wcs.domain.task.WorkflowStepDefinition;
|
||||||
|
import com.ruoyi.wcs.port.out.DomainEventPublisher;
|
||||||
|
import com.ruoyi.wcs.port.out.TransportOrderRepository;
|
||||||
|
import com.ruoyi.wcs.port.out.WorkflowDefinitionProvider;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class TransportOrderApplicationServiceTest {
|
||||||
|
private InMemoryRepository repository;
|
||||||
|
private RecordingPublisher publisher;
|
||||||
|
private TransportOrderApplicationService service;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
repository = new InMemoryRepository();
|
||||||
|
publisher = new RecordingPublisher();
|
||||||
|
service = new TransportOrderApplicationService(
|
||||||
|
repository,
|
||||||
|
new SingleStepDefinitions(),
|
||||||
|
publisher,
|
||||||
|
new SequenceIdGenerator(),
|
||||||
|
new FixedClock());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void returnsTheExistingOrderForTheSameExternalRequest() {
|
||||||
|
TransportOrderResult first = service.create(command("REQ-1"));
|
||||||
|
TransportOrderResult second = service.create(command("REQ-1"));
|
||||||
|
|
||||||
|
assertEquals(first.getOrderId(), second.getOrderId());
|
||||||
|
assertEquals(1, repository.count());
|
||||||
|
assertEquals(2, publisher.events.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createsAPlannedOrderAndItsWorkflowAtomically() {
|
||||||
|
TransportOrderResult result = service.create(command("REQ-2"));
|
||||||
|
|
||||||
|
assertEquals(TransportOrderState.PLANNED, result.getState());
|
||||||
|
assertEquals("PORT-1", result.getSourceCode());
|
||||||
|
assertEquals("SPACE-9", result.getDestinationCode());
|
||||||
|
assertEquals(1, repository.workflows.size());
|
||||||
|
assertEquals("OrderAccepted", publisher.events.get(0).getType());
|
||||||
|
assertEquals("OrderPlanned", publisher.events.get(1).getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void cancelBeforeExecutionProducesOneCancelledTransition() {
|
||||||
|
service.create(command("REQ-3"));
|
||||||
|
|
||||||
|
TransportOrderResult cancelled = service.cancel("REQ-3");
|
||||||
|
|
||||||
|
assertEquals(TransportOrderState.CANCELLED, cancelled.getState());
|
||||||
|
assertEquals("OrderCancelled", publisher.events.get(2).getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private CreateTransportOrderCommand command(String requestId) {
|
||||||
|
return new CreateTransportOrderCommand(
|
||||||
|
requestId,
|
||||||
|
TransportOrderType.INBOUND,
|
||||||
|
"LOAD-1",
|
||||||
|
"PORT-1",
|
||||||
|
"SPACE-9",
|
||||||
|
50,
|
||||||
|
null,
|
||||||
|
Collections.<String, String>emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class FixedClock implements WcsClock {
|
||||||
|
@Override
|
||||||
|
public Instant now() {
|
||||||
|
return Instant.parse("2026-08-04T00:00:00Z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class SequenceIdGenerator implements IdGenerator {
|
||||||
|
private int next = 1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String nextId() {
|
||||||
|
return "ID-" + next++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class SingleStepDefinitions implements WorkflowDefinitionProvider {
|
||||||
|
@Override
|
||||||
|
public WorkflowDefinition definitionFor(TransportOrderType orderType) {
|
||||||
|
return new WorkflowDefinition(orderType, Collections.singletonList(
|
||||||
|
new WorkflowStepDefinition("MOVE", TaskStepType.MOVE,
|
||||||
|
true, true, Collections.<String>emptyList())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RecordingPublisher implements DomainEventPublisher {
|
||||||
|
private final List<DomainEvent> events = new ArrayList<DomainEvent>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publish(List<DomainEvent> domainEvents) {
|
||||||
|
events.addAll(domainEvents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class InMemoryRepository implements TransportOrderRepository {
|
||||||
|
private final Map<String, TransportOrder> orders =
|
||||||
|
new LinkedHashMap<String, TransportOrder>();
|
||||||
|
private final Map<String, WorkflowInstance> workflows =
|
||||||
|
new LinkedHashMap<String, WorkflowInstance>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransportOrder findByExternalRequestId(String externalRequestId) {
|
||||||
|
return orders.get(externalRequestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransportOrder findByOrderId(String orderId) {
|
||||||
|
for (TransportOrder order : orders.values()) {
|
||||||
|
if (order.getOrderId().equals(orderId)) {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(TransportOrder order, WorkflowInstance workflow) {
|
||||||
|
orders.put(order.getExternalRequestId(), order);
|
||||||
|
if (workflow != null) {
|
||||||
|
workflows.put(workflow.getWorkflowId(), workflow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(TransportOrder order) {
|
||||||
|
orders.put(order.getExternalRequestId(), order);
|
||||||
|
}
|
||||||
|
|
||||||
|
int count() {
|
||||||
|
return orders.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user