DDD & REST — Domain-Driven APIs for the web

DDD & REST — Domain-Driven APIs for the web

Slides of the talk I gave at JavaLand 2017.

@springcentral

977c74bb044a9d4fa90b305824eda390?s=128

Oliver Drotbohm

March 28, 2017
Tweet

Transcript

  1. DDD & REST Domain-Driven APIs for the web / olivergierke

    Oliver Gierke ogierke@pivotal.io
  2. None
  3. When designing REST APIs, what can we learn from DDD?

    “ 3
  4. What does it take to bridge the worlds of DDD

    & REST? “ 4
  5. None
  6. 6 http://www.infoq.com/minibooks/domain-driven-design-quickly

  7. 7

  8. Value objects 8

  9. Stringly typed code ! 9 class Customer { private Long

    id; private String firstname, lastname, email; … }
  10. Stringly typed code ! 10 class SomeService { void createUser(…,

    String email) { // Valid email address? … } }
  11. 11 class Customer { private Long id; private Firstname firstname;

    private Lastname lastname; private EmailAddress emailAddress; … }
  12. Stringly typed code ! 12 class SomeService { void createUser(…,

    String email) { // Valid email address? } }
  13. Strongly typed code " 13 class SomeService { void createUser(…,

    EmailAddress email) { // Valid email address! } }
  14. Make implicit
 concepts explicit. 14

  15. Entities,
 Aggregates & Repositories 15

  16. 16 Order LineItem Product Invoice Customer Payment Address Email

  17. Don’t get trapped by datastore thinking. 17

  18. 19 Order LineItem Product Invoice Customer Payment Address Email

  19. Aggregates.
 Scopes of consistency. 20

  20. 21 Order LineItem Product Invoice Customer Payment Address Email

  21. Aggregates.
 The key things
 to refer to. 22

  22. 23 Order LineItem Product Invoice Customer Payment Address Email

  23. Bounded Context 24

  24. Order LineItem Product Invoice Customer Payment Address

  25. 26 Shipping Accounting Catalog Orders User
 Registration

  26. Domain Events 27

  27. Explicitness
 maturity model 28

  28. 29 Level 0: CRUD

  29. 30 void addToOrder(Order order, Product product, Quantity quantity) { order.items.stream()


    .filter(it -> it.product.equals(product) .ifPresentOrElse( it -> it.increase(quantity), order.items.add(new LineItem(product, quantity)) ); }
  30. 30 void addToOrder(Order order, Product product, Quantity quantity) { order.items.stream()


    .filter(it -> it.product.equals(product) .ifPresentOrElse( it -> it.increase(quantity), order.items.add(new LineItem(product, quantity)) ); }
  31. 30 void addToOrder(Order order, Product product, Quantity quantity) { order.items.stream()


    .filter(it -> it.product.equals(product) .ifPresentOrElse( it -> it.increase(quantity), order.items.add(new LineItem(product, quantity)) ); } Aggregate code in clients !
  32. 31 Level 0: CRUD Level 1: Explicit operations

  33. 32 class Order { Order add(Product product, Quantity quantity) {

    this.items.stream()
 .filter(it -> it.product.equals(product) .ifPresentOrElse( it -> it.increase(quantity), order.items.add(new LineItem(product, quantity)) ); } }
  34. 32 class Order { Order add(Product product, Quantity quantity) {

    this.items.stream()
 .filter(it -> it.product.equals(product) .ifPresentOrElse( it -> it.increase(quantity), order.items.add(new LineItem(product, quantity)) ); } } Aggregate code in aggregate "
  35. 33 Level 0: CRUD Level 1: Explicit operations Level 2:

    Some operations as events
  36. 34 class OrderManagement { private final OrderRepository orders; private final

    Inventory inventory; @Transactional void completeOrder(Order order) { orders.save(order.complete()); order.doWithLineItems(item -> inventory.reduce(item.product, item.quantity)); } }
  37. Feature creep 35

  38. 36 class OrderManagement { private final OrderRepository orders; private final

    Inventory inventory; private final LoyaltyProgram loyalty; @Transactional void complete(Order order) { orders.save(order.complete()); order.doWithLineItems(item -> inventory.reduce(item.product, item.quantity)); loyalty.update(order); } }
  39. Synchronous, in-process
 remote communication 37 aka. „The Careless Microservice“

  40. 38 class OrderManagement { private final OrderRepository orders; private final

    RestOperations operations; @Transactional void completeOrder(Order order) { orders.save(order.complete()); // Update external systems operations.postForEntity(…); operations.postForEntity(…); } }
  41. … and then we started to throw async, reactive and

    Hystrix at it. “ 39
  42. … we tried to solve an architectural problem by adding

    more tech. “ 40
  43. Domain events 41

  44. 42 class OrderManagement { private final OrderRepository orders; private final

    ApplicationEventPublisher publisher; @Transactional void completeOrder(Order order) { OrderCompletedEvent event = order.complete(); orders.save(order); publisher.publish(event); } }
  45. 43 class Inventory { private final InventoryItemRepository items; @EventListener void

    on(OrderCompletedEvent order) { // Update inventory } }
  46. 44 class EmailNotification { private final MailSender mailSender; @Async @TransactionalEventListener

    void on(OrderCompletedEvent order) { // Send emails } }
  47. 45 class OrderManagement { private final OrderRepository orders; private final

    ApplicationEventPublisher publisher; @Transactional void completeOrder(Order order) { OrderCompletedEvent event = order.complete(); orders.save(order); publisher.publish(event); } }
  48. Move event creation
 to aggregates 46

  49. 47 // Super class contains methods with // @DomainEvents und

    @AfterDomainEventPublication class Order extends AbstractAggregateRoot { Order complete() { register(new OrderCompletedEvent(this)); return this; } }
  50. 48 class OrderManagement { private final OrderRepository orders; void completeOrder(Order

    order) { repository.save(order.complete()); } }
  51. 48 class OrderManagement { private final OrderRepository orders; void completeOrder(Order

    order) { repository.save(order.complete()); } } The aggregate is in charge "
  52. 49 Level 0: CRUD Level 1: Explicit operations Level 2:

    Some operations as events Level 3: CQRS / ES
  53. REST 50

  54. REST ≠ 
 CRUD via HTTP 51

  55. Aggregates Identifiable
 Referable
 Scope of consistency 52

  56. Resources Identifiable
 Referable
 Scope of consistency 53

  57. Representation
 design matters 54

  58. Hypermedia 55

  59. Serving data and navigation information
 at the same time. 56

  60. Hypermedia as
 the engine of
 application state 57

  61. RESTBucks payment expected preparing cancelled ready completed 1 2 3

    4 5 6
  62. Method URI Action Step POST /orders Create new order 1

    POST/PATCH /orders/{id} Update the order (only if "payment expected") 2 DELETE /orders/{id} Cancel order (only if "payment expected") 3 PUT /orders/{id}/payment Pay order (only if "payment expected") 4 Barista preparing the order GET /orders/{id} Poll order state 5 GET /orders/{id}/receipt Access receipt DELETE /orders/{id}/receipt Conclude the order process 6
  63. How does the client
 know if a state
 transition is

    allowed? 60
  64. Option 1:
 Inspecting the payload 61

  65. 63 GET /order/4711 { „createdDate“ : …, „status“ : „Payment

    expected“ … }
  66. 64 “Internationalize
 all user facing text!

  67. Aaaargh! 65

  68. Option 2:
 Inspecting
 hypermedia elements 66

  69. Method URI Action Step POST /orders Create new order 1

    POST/PATCH /orders/{id} Update the order (only if "payment expected") 2 DELETE /orders/{id} Cancel order (only if "payment expected") 3 PUT /orders/{id}/payment Pay order (only if "payment expected") 4 Barista preparing the order GET /orders/{id} Poll order state 5 GET /orders/{id}/receipt Access receipt DELETE /orders/{id}/receipt Conclude the order process 6
  70. Method Resource type Action Step POST orders Create new order

    1 POST/PATCH update Update the order 2 DELETE cancel Cancel order 3 PUT payment Pay order 4 Barista preparing the order GET order Poll order state 5 GET receipt Access receipt DELETE receipt Conclude the order process 6
  71. 69 GET /order/42 { „_links“ : { „cancel“ : {

    „href“ : … }, … „createdDate“ : …, „status“ : „Payment expected“ … }
  72. Reducing decisions in clients to whether a
 link is present

    or not. 70
  73. Trading domain knowledge with protocol complexity in clients. 71

  74. 72 Amount of domain knowledge in the client Amount of

    protocol knowledge in the client Coupling to the server Non-hypermedia
 based systems Hypermedia
 based systems
  75. API evolvability is key
 in a system of systems 73

    See „Evolving Distributed Systems“
  76. RESTBucks payment expected preparing cancelled ready completed 1 2 3

    4 5 6
  77. Spring RESTBucks 75

  78. Web Service Repository - Orders Spring Data Spring Data
 REST

    Payment Spring Data Manual
 implementation Manual
 implementation
  79. Resources 77 Spring RESTBucks
 https://github.com/olivergierke/spring-restbucks Benefits of Hypermedia APIs
 http://olivergierke.de/2016/04/benefits-of-hypermedia/

    Evolving Distributed Systems http://olivergierke.de/2016/10/evolving-distributed-systems/
  80. Questions? 78