Separate write/read DataSources, a JPA command side, and two interchangeable projection listeners (sync and @Async) demonstrating the real latency-versus- freshness trade-off CQRS forces. Includes a reflection-based proof that the query side has no dependency on the write side, and a real failure/fix transcript for the -parameters compiler flag this standalone reactor doesn't inherit from spring-boot-starter-parent.
52 lines
2.6 KiB
Java
52 lines
2.6 KiB
Java
package com.ankurm.cqrsdemo;
|
|
|
|
import com.ankurm.cqrsdemo.command.OrderCommandController.LineRequest;
|
|
import com.ankurm.cqrsdemo.command.OrderCommandController.PlaceOrderRequest;
|
|
import com.ankurm.cqrsdemo.command.OrderCommandController.PlaceOrderResponse;
|
|
import com.ankurm.cqrsdemo.query.OrderSummary;
|
|
import org.junit.jupiter.api.Test;
|
|
import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate;
|
|
import org.springframework.boot.resttestclient.TestRestTemplate;
|
|
import org.springframework.boot.test.context.SpringBootTest;
|
|
import org.springframework.http.ResponseEntity;
|
|
|
|
import java.util.List;
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
|
|
/**
|
|
* Default profile: no {@code async-projection}, so {@link com.ankurm.cqrsdemo.read.SyncOrderSummaryProjection}
|
|
* is active. The projection's simulated write delay (300ms, see {@code application.properties})
|
|
* runs on the same thread that is about to answer the command's HTTP request, so it shows up
|
|
* directly in how long {@code POST /orders} takes -- and because of that, the read model is
|
|
* already correct the instant the command returns, with no waiting required.
|
|
*/
|
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|
@AutoConfigureTestRestTemplate
|
|
class SyncProfileLatencyTest {
|
|
|
|
@org.springframework.beans.factory.annotation.Autowired
|
|
private TestRestTemplate rest;
|
|
|
|
@Test
|
|
void placingAnOrderTakesAtLeastTheSimulatedProjectionDelayAndTheReadModelIsImmediatelyConsistent() {
|
|
PlaceOrderRequest request = new PlaceOrderRequest("Priya",
|
|
List.of(new LineRequest("USB-C-CABLE", 2, 1299), new LineRequest("USB-C-HUB", 1, 4999)));
|
|
|
|
long start = System.currentTimeMillis();
|
|
ResponseEntity<PlaceOrderResponse> placed = rest.postForEntity("/orders", request, PlaceOrderResponse.class);
|
|
long elapsedMs = System.currentTimeMillis() - start;
|
|
|
|
String orderId = placed.getBody().orderId();
|
|
System.out.println("POST /orders (sync profile) took " + elapsedMs + " ms, orderId=" + orderId);
|
|
assertThat(elapsedMs).isGreaterThanOrEqualTo(300L);
|
|
|
|
ResponseEntity<OrderSummary> summary = rest.getForEntity("/order-summaries/" + orderId, OrderSummary.class);
|
|
System.out.println("Immediately after POST returned, GET /order-summaries/" + orderId
|
|
+ " -> status=" + summary.getStatusCode() + ", body=" + summary.getBody());
|
|
assertThat(summary.getBody()).isNotNull();
|
|
assertThat(summary.getBody().totalCents()).isEqualTo(2 * 1299 + 4999);
|
|
assertThat(summary.getBody().status()).isEqualTo("PLACED");
|
|
}
|
|
}
|