Skip to content
Nomnoml templates

Package diagram — ports and adapters, with the ArchUnit rule that enforces it

UML package diagram of a Java service laid out as hexagonal / ports-and-adapters architecture, showing every compile-time «use» and «realise» dependency between adapter, application and domain packages together with the ArchUnit layeredArchitecture test that fails the build when an arrow points outward.

Template previewNomnoml
Rendering…

Make it your own.

#direction: right
#spacing: 46
#padding: 10
#fontSize: 11
#edges: rounded
[<frame>com.acme.orders — ports and adapters|
  [<package>adapter.in.web|
    OrderController
    PlaceOrderRequest
    OrderResponseMapper]
  [<package>adapter.in.messaging|
    PaymentAuthorisedListener
    OrderEventDeserializer]
  [<package>application.port.in|
    «interface» PlaceOrderUseCase
    «interface» CancelOrderUseCase]
  [<package>application.port.out|
    «interface» LoadOrderPort
    «interface» SaveOrderPort
    «interface» AuthorisePaymentPort]
  [<package>application.service|
    PlaceOrderService
    CancelOrderService]
  [<package>domain.model|
    Order
    OrderLine
    Money
    OrderStatus]
  [<package>adapter.out.persistence|
    OrderJpaRepository
    OrderEntity
    OrderEntityMapper]
  [<package>adapter.out.payment|
    StripePaymentGateway
    StripeErrorTranslator]

  [adapter.in.web] --> [application.port.in]
  [adapter.in.messaging] --> [application.port.in]
  [application.service] --:> [application.port.in]
  [application.service] --> [application.port.out]
  [application.service] --> [domain.model]
  [adapter.out.persistence] --:> [application.port.out]
  [adapter.out.payment] --:> [application.port.out]
  [adapter.out.persistence] --> [domain.model]
  [application.port.in] --> [domain.model]
  [application.port.out] --> [domain.model]
]
[<note>Enforced by OrdersArchitectureTest (ArchUnit 1.x):
layeredArchitecture().consideringAllDependencies()
 .layer("domain").definedBy("com.acme.orders.domain..")
 .layer("application").definedBy("com.acme.orders.application..")
 .layer("adapter").definedBy("com.acme.orders.adapter..")
 .whereLayer("adapter").mayNotBeAccessedByAnyLayer()
 .whereLayer("application").mayOnlyBeAccessedByLayers("adapter")
 .whereLayer("domain").mayOnlyBeAccessedByLayers("application", "adapter")

Dashed open arrows are «use» dependencies, hollow triangles «realise».
Every arrow points inward: domain.model imports nothing but the JDK.] -- [<frame>com.acme.orders — ports and adapters]