Rétegek rendezése hatszögletű architektúra, DDD és Spring segítségével

Java Top

Most jelentettem be az újat Tanulj tavaszt tanfolyam, amelynek középpontjában az 5. tavasz és a tavaszi bakancs 2 alapjai állnak:

>> ELLENŐRIZZE A FOLYAMATOT

1. Áttekintés

Ebben az oktatóanyagban egy tavaszi alkalmazást valósítunk meg a DDD használatával. Ezenkívül rétegeket rendezünk a Hexagonal Architecture segítségével.

Ezzel a megközelítéssel könnyen kicserélhetjük az alkalmazás különböző rétegeit.

2. Hatszögletű építészet

A hatszögletű építészet a modellje szoftveralkalmazások tervezése a tartományi logika köré elkülöníteni a külső tényezőktől.

A tartományi logikát egy üzleti mag határozza meg, amelyet belső résznek fogunk hívni, a többi pedig külső rész. A tartományi logikához kívülről hozzáférés portokon és adaptereken keresztül érhető el.

3. Alapelvek

Először is meg kell határoznunk az elveket a kódunk felosztásához. Amint azt röviden kifejtettük, a hatszögletű architektúra meghatározza a belső és a külső rész.

Ehelyett azt tesszük, hogy három rétegre osztjuk az alkalmazásunkat; alkalmazás (kívül), tartomány (belül) és infrastruktúra (kívül):

Keresztül az alkalmazásréteg, a felhasználó vagy bármely más program kölcsönhatásba lép az alkalmazással. Ennek a területnek tartalmaznia kell például a felhasználói felületeket, a RESTful vezérlőket és a JSON sorosítási könyvtárakat. Bármit tartalmaz amely kiteszi a belépést az alkalmazásunkba és összehangolja a tartományi logika végrehajtását.

A tartományrétegben megtartjuk azt a kódot, amely megérinti és megvalósítja az üzleti logikát. Ez az alkalmazásunk lényege. Ezenkívül ezt a réteget el kell különíteni mind az alkalmazás, mind az infrastruktúra részektől. Ezenkívül tartalmaznia kell olyan interfészeket is, amelyek meghatározzák az API-t a külső részekkel való kommunikációhoz, például az adatbázishoz, amellyel a tartomány kölcsönhatásba lép.

Végül a Az infrastruktúra réteg az a rész, amely bármit tartalmaz, amire az alkalmazásnak szüksége van mint például az adatbázis-konfiguráció vagy a Spring-konfiguráció. Emellett infrastruktúra-függő interfészeket is megvalósít a tartományrétegből.

4. Domainréteg

Kezdjük az alaprétegünk megvalósításával, amely a tartományréteg.

Először létre kell hoznunk a Rendelés osztály:

public class Rendelés {private UUID azonosító; privát OrderStatus állapot; private List orderItems; privát BigDecimal ár; public Order (UUID azonosító, Terméktermék) {this.id = id; this.orderItems = new ArrayList (Arrays.astList (new OrderItem (product))); this.status = OrderStatus.CREATED; this.price = product.getPrice (); } public void teljes () {validateState (); this.status = OrderStatus.COMPLETED; } public void addOrder (Terméktermék) {validateState (); validateProduct (termék); orderItems.add (új OrderItem (termék)); price = price.add (product.getPrice ()); } public void removeOrder (UUID azonosító) {validateState (); végleges OrderItem orderItem = getOrderItem (id); orderItems.remove (orderItem); ár = ár.kivonás (orderItem.getPrice ()); } // getters}

Ez az összesített gyökér. Minden, ami üzleti logikánkkal kapcsolatos, ezen az osztályon megy keresztül. Ezenkívül Rendelés felelős azért, hogy a megfelelő állapotban tartsa magát:

  • A megrendelés csak a megadott azonosítóval hozható létre, és annak alapján Termék - maga a konstruktor is beilleszti a sorrendet LÉTREHOZOTT állapot
  • Miután a megrendelés elkészült, változik OrderItems lehetetlen
  • Lehetetlen megváltoztatni a Rendelés a tartomány objektumon kívülről, például egy szetterrel

Továbbá a Rendelés osztály felelős a létrehozásáért is OrderItem.

Hozzuk létre a OrderItem osztály akkor:

public class OrderItem {private UUID productId; privát BigDecimal ár; public OrderItem (Terméktermék) {this.productId = product.getId (); this.price = product.getPrice (); } // getters}

Ahogy látjuk, OrderItem alapján jön létre Termék. Megőrzi a hivatkozást, és tárolja a Termék.

Ezután létrehozunk egy lerakat felületet (a kikötő a hatszögletű építészetben). Az interfész megvalósítása az infrastruktúra rétegben történik:

nyilvános felület OrderRepository {Opcionális findById (UUID id); void save (Rendelési sorrend); }

Végül meg kell győződnünk arról, hogy a Rendelés minden akció után mindig mentésre kerül. Ehhez, meghatározunk egy tartományi szolgáltatást, amely általában olyan logikát tartalmaz, amely nem lehet része a gyökérzetünknek:

public class DomainOrderService végrehajtja a OrderService {private final OrderRepository orderRepository; public DomainOrderService (OrderRepository orderRepository) {this.orderRepository = orderRepository; } @Orride public UUID createOrder (Product product) {Order order = new Order (UUID.randomUUID (), product); orderRepository.save (rendelés); return order.getId (); } @Orride public void addProduct (UUID id, Product product) {Rendelési sorrend = getOrder (id); order.addRendelés (termék); orderRepository.save (rendelés); } @Orride public void completeOrder (UUID id) {Order order = getOrder (id); Rendelés kész(); orderRepository.save (rendelés); } @Orride public void deleteProduct (UUID azonosító, UUID productId) {Rendelési sorrend = getOrder (id); order.removeOrder (productId); orderRepository.save (rendelés); } private order getOrder (UUID id) {return orderRepository .findById (id) .orElseThrow (RuntimeException :: new); }}

Hatszögletű architektúrában ez a szolgáltatás a portot megvalósító adapter. Ezenkívül nem fogjuk regisztrálni tavaszi babkéntmert tartomány szempontjából ez a belső részen van, a Spring konfiguráció pedig kívül. Kicsit később kézzel fogjuk bekötni a Springrel az infrastruktúra rétegbe.

Mivel a tartományréteg teljesen leválasztott az alkalmazási és az infrastrukturális rétegekből, mitud is tesztelje önállóan:

class DomainOrderServiceUnitTest {private OrderRepository orderRepository; privát DomainOrderService tesztelve; @BeforeEach void setUp () {orderRepository = gúny (OrderRepository.class); tesztelt = new DomainOrderService (orderRepository); } @Test void shouldCreateOrder_thenSaveIt () {végtermék termék = új termék (UUID.randomUUID (), BigDecimal.TEN, "productName"); végső UUID azonosító = tesztelt.createOrder (termék); ellenőrizze (orderRepository) .save (bármilyen (Order.class)); assertNotNull (id); }}

5. Alkalmazási réteg

Ebben a szakaszban valósítjuk meg az alkalmazási réteget. Lehetővé tesszük a felhasználó számára, hogy az alkalmazásunkkal egy RESTful API-n keresztül kommunikáljon.

Ezért hozzuk létre a OrderController:

@RestController @RequestMapping ("/ megrendelések") public class OrderController {private OrderService orderService; @Autowired public OrderController (OrderService orderService) {this.orderService = orderService; } @ PostMapping CreateOrderResponse createOrder (@RequestBody CreateOrderRequest kérés) {UUID id = orderService.createOrder (request.getProduct ()); return new CreateOrderResponse (id); } @PostMapping (value = "/ {id} / products") void addProduct (@PathVariable UUID azonosító, @RequestBody AddProductRequest kérelem) {orderService.addProduct (id, request.getProduct ()); } @DeleteMapping (value = "/ {id} / products") void deleteProduct (@PathVariable UUID azonosító, @RequestParam UUID productId) {orderService.deleteProduct (id, productId); } @PostMapping ("/ {id} / complete") void completeOrder (@PathVariable UUID id) {orderService.completeOrder (id); }}

Ez az egyszerű Spring Rest vezérlő felelős a tartományi logika végrehajtásának összehangolásáért.

Ez a vezérlő a külső RESTful felületet igazítja a tartományunkhoz. A megfelelő módszerek meghívásával innen teszi OrderService (kikötő).

6. Infrastruktúra réteg

Az infrastruktúra réteg tartalmazza az alkalmazás futtatásához szükséges logikát.

Ezért kezdjük a konfigurációs osztályok létrehozásával. Először valósítsunk meg egy osztályt, amely regisztrálja az osztályunkat OrderService mint tavaszi bab:

@Configuration public class BeanConfiguration {@Bean OrderService orderService (OrderRepository orderRepository) {return new DomainOrderService (orderRepository); }}

Ezután hozzuk létre azt a konfigurációt, amely felelős az általunk használt Spring Data tárolók engedélyezéséért:

@EnableMongoRepositories (basePackageClasses = SpringDataMongoOrderRepository.class) public class MongoDBConfiguration {}

Használtuk a basePackageClasses ingatlan, mert ezek az adattárak csak az infrastruktúra rétegében lehetnek. Ezért nincs oka annak, hogy Spring az egész alkalmazást beolvassa. Ezenkívül ez az osztály tartalmazhat mindent, ami a MongoDB és az alkalmazásunk közötti kapcsolat létrehozásához kapcsolódik.

Végül megvalósítjuk a OrderRepository a tartományrétegből. Majd felhasználjuk SpringDataMongoOrderRepository megvalósításunkban:

@Component public class A MongoDbOrderRepository végrehajtja a OrderRepository {private SpringDataMongoOrderRepository orderRepository; @Autowired public MongoDbOrderRepository (SpringDataMongoOrderRepository orderRepository) {this.orderRepository = orderRepository; } @Override public Opcionális findById (UUID id) {return orderRepository.findById (id); } @Orride public void save (Order order) {orderRepository.save (order); }}

Ez a megvalósítás tárolja a mi Rendelés a MongoDB-ben. Hatszögletű architektúrában ez a megvalósítás egyben adapter is.

7. Előnyök

Ennek a megközelítésnek az első előnye, hogy mi különálló munka minden réteghez. Egy rétegre koncentrálhatunk anélkül, hogy másokat érintenénk.

Ezenkívül természetesen könnyebben érthetőek, mert mindegyikük a logikájára összpontosít.

Egy másik nagy előny, hogy a domain logikáját elkülönítettük minden mástól. A tartományrész csak üzleti logikát tartalmaz, és könnyen áthelyezhető egy másik környezetbe.

Valójában változtassuk meg az infrastrukturális réteget, hogy a Cassandra-t adatbázisként használjuk:

@Component public class CassandraDbOrderRepository implementálja a OrderRepository {private final SpringDataCassandraOrderRepository orderRepository; @Autowired public CassandraDbOrderRepository (SpringDataCassandraOrderRepository orderRepository) {this.orderRepository = orderRepository; } @Override public Opcionális findById (UUID id) {Opcionális orderEntity = orderRepository.findById (id); if (orderEntity.isPresent ()) {return Optional.of (orderEntity.get () .toOrder ()); } else {return Opcionális.empty (); }} @Orride public void save (Order order) {orderRepository.save (new OrderEntity (order)); }}

A MongoDB-vel ellentétben most egy OrderEntity hogy a tartomány megmaradjon az adatbázisban.

Ha hozzáadjuk a technológiához kapcsolódó jegyzeteket Rendelés domain objektum, azután megsértjük az infrastruktúra és a domain rétegek elválasztását.

Az adattár a domaint a kitartási igényeinkhez igazítja.

Menjünk egy lépéssel tovább, és alakítsuk át a RESTful alkalmazást parancssori alkalmazássá:

@Component public class CliOrderController {private static final Logger LOG = LoggerFactory.getLogger (CliOrderController.class); privát végleges OrderService orderService; @Autowired public CliOrderController (OrderService orderService) {this.orderService = orderService; } public void createCompleteOrder () {LOG.info ("<>"); UUID orderId = createOrder (); orderService.completeOrder (orderId); } public void createIncompleteOrder () {LOG.info ("<>"); UUID orderId = createOrder (); } private UUID createOrder () {LOG.info ("Új termék leadása két termékkel"); Product mobilePhone = new Product (UUID.randomUUID (), BigDecimal.valueOf (200), "mobile"); Termékborotva = új termék (UUID.randomUUID (), BigDecimal.valueOf (50), "borotva"); LOG.info ("Rendelés létrehozása mobiltelefonnal"); UUID orderId = orderService.createOrder (mobilePhone); LOG.info ("Borotva hozzáadása a megrendeléshez"); orderService.addProduct (orderId, borotva); return orderId; }}

A korábbiaktól eltérően most előre meghatározott műveletek halmazát vezetjük be, amelyek kölcsönhatásba lépnek a tartományunkkal. Ezt felhasználhatnánk arra, hogy például csúfolt adatokkal töltsük fel alkalmazásunkat.

Annak ellenére, hogy teljesen megváltoztattuk az alkalmazás célját, nem érintettük a tartományréteget.

8. Következtetés

Ebben a cikkben megtanultuk, hogyan különítsük el az alkalmazásunkhoz kapcsolódó logikát meghatározott rétegekre.

Először három fő réteget határoztunk meg: alkalmazást, tartományt és infrastruktúrát. Ezt követően ismertettük, hogyan lehet ezeket kitölteni, és ismertettük az előnyöket.

Ezután kidolgoztuk az egyes rétegek megvalósítását:

Végül felcseréltük az alkalmazás és az infrastruktúra rétegeit anélkül, hogy befolyásolnánk a tartományt.

Mint mindig, ezeknek a példáknak a kódja elérhető a GitHub oldalon.

Java alsó

Most jelentettem be az újat Tanulj tavaszt tanfolyam, amelynek középpontjában az 5. tavasz és a tavaszi bakancs 2 alapjai állnak:

>> ELLENŐRIZZE A FOLYAMATOT

$config[zx-auto] not found$config[zx-overlay] not found