제한된 컨텍스트는 도메인 기반 디자인(DDD)의 핵심 패턴 중 하나입니다. 대규모 프로젝트를 도메인으로 나누는 방법을 나타냅니다. 이러한 분리를 통해 유연성이 향상되고 유지 관리가 더 쉬워집니다.
육각형 아키텍처는 애플리케이션의 핵심을 외부 종속성과 분리합니다. 포트와 어댑터를 사용하여 비즈니스 로직을 외부 서비스와 분리합니다. 비즈니스 로직을 프레임워크, 데이터베이스 또는 사용자 인터페이스와 독립적으로 만들면 애플리케이션이 향후 요구 사항에 쉽게 적응할 수 있습니다.
아키텍처는 세 가지 주요 구성 요소로 구성됩니다.
이 연습 프로젝트는 Java의 제한된 컨텍스트와 육각형 아키텍처를 사용합니다.
Techtopia라는 놀이공원의 티켓팅 시스템을 만드는 것이 목표입니다. 이 프로젝트에는 티켓, 명소, 입구 게이트라는 3가지 주요 경계 컨텍스트가 있습니다. 각 경계 컨텍스트에는 해당 디렉터리가 있으며 포트, 어댑터, 사용 사례 등과 같은 구성 요소가 포함됩니다.
파크 티켓 구매 코드 프로세스를 살펴보겠습니다.
" 도메인 " 디렉토리를 생성하고 프레임워크나 외부 종속성이 없는 비즈니스 로직을 포함합니다.
"티켓" 엔터티를 만듭니다.
package java.boundedContextA.domain; import lombok.Getter; import lombok.Setter; import lombok.ToString; import java.time.Duration; import java.time.LocalDateTime; import java.util.UUID; @Getter @Setter @ToString public class Ticket { private TicketUUID ticketUUID; private LocalDateTime start; private LocalDateTime end; private double price; private TicketAction ticketAction; private final Guest.GuestUUID owner; private ActivityWindow activityWindow; public record TicketUUID(UUID uuid) { } public Ticket(TicketUUID ticketUUID, Guest.GuestUUID owner) { this.ticketUUID = ticketUUID; this.owner = owner; } public Ticket(TicketUUID ticketUUID, LocalDateTime start, LocalDateTime end, double price, TicketAction ticketAction, Guest.GuestUUID owner) { this.ticketUUID = ticketUUID; this.start = start; this.end = end; this.price = price; this.ticketAction = ticketAction; this.owner = owner; } public Ticket(TicketUUID ticketUUID, LocalDateTime start, LocalDateTime end, double price, Guest.GuestUUID owner, ActivityWindow activityWindow) { this.ticketUUID = ticketUUID; this.start = start; this.end = end; this.price = price; this.owner = owner; this.activityWindow = activityWindow; } public void addTicketActivity(TicketActivity ticketActivity) { this.activityWindow.add(ticketActivity); } }
또한 "BuyTicket"이라는 또 다른 도메인 클래스를 만듭니다.
package java.boundedContextA.domain; import lombok.Getter; import lombok.Setter; import lombok.ToString; import java.time.Duration; import java.time.LocalDateTime; import java.util.UUID; @Getter @Setter @ToString public class Ticket { private TicketUUID ticketUUID; private LocalDateTime start; private LocalDateTime end; private double price; private TicketAction ticketAction; private final Guest.GuestUUID owner; private ActivityWindow activityWindow; public record TicketUUID(UUID uuid) { } public Ticket(TicketUUID ticketUUID, Guest.GuestUUID owner) { this.ticketUUID = ticketUUID; this.owner = owner; } public Ticket(TicketUUID ticketUUID, LocalDateTime start, LocalDateTime end, double price, TicketAction ticketAction, Guest.GuestUUID owner) { this.ticketUUID = ticketUUID; this.start = start; this.end = end; this.price = price; this.ticketAction = ticketAction; this.owner = owner; } public Ticket(TicketUUID ticketUUID, LocalDateTime start, LocalDateTime end, double price, Guest.GuestUUID owner, ActivityWindow activityWindow) { this.ticketUUID = ticketUUID; this.start = start; this.end = end; this.price = price; this.owner = owner; this.activityWindow = activityWindow; } public void addTicketActivity(TicketActivity ticketActivity) { this.activityWindow.add(ticketActivity); } }
*BuyTicket *은 티켓 구매 논리를 나타냅니다. 별도의 Spring 구성 요소로 만들면 다른 구성 요소와 독립적으로 발전할 수 있는 티켓 구매 논리를 해당 클래스로 격리할 수 있습니다. 이렇게 분리하면 유지 관리성이 향상되고 코드베이스가 더욱 모듈화됩니다.
"ports/in" 디렉터리에서 사용 사례를 만듭니다. 여기서는 티켓을 구매하는 활용 사례를 만들어 보겠습니다.
package java.boundedContextA.domain; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.UUID; @Component public class BuyTicket { public Ticket buyTicket(TicketAction ticketAction, LocalDateTime start, LocalDateTime end, double price, Guest.GuestUUID owner) { return new Ticket(new Ticket.TicketUUID(UUID.randomUUID()), start, end, price, ticketAction, owner); } }
티켓의 기록을 만들어 저장하세요.
package java.boundedContextA.ports.in; public interface BuyingATicketUseCase { void buyTicket(BuyTicketsAmountCommand buyTicketsAmountCommand); }
다음으로 "ports/out" 디렉토리에서 해당 티켓 구매의 각 단계를 나타내는 포트를 생성합니다. "CreateTicketPort", "TicketLoadPort", "TicketUpdatePort".
와 같은 인터페이스를 생성하세요.
package java.boundedContextA.ports.in; import java.boundedContextA.domain.Guest; import java.boundedContextA.domain.TicketAction; import java.time.LocalDateTime; public record BuyTicketsAmountCommand(double price, TicketAction action, LocalDateTime start, LocalDateTime end, Guest.GuestUUID owner) {}
"core"라는 별도의 디렉토리에 티켓 구매 사용 사례의 인터페이스를 구현합니다.
package java.boundedContextA.ports.out; import java.boundedContextA.domain.Ticket; public interface TicketCreatePort { void createTicket(Ticket ticket); }
"adapters/out" 디렉터리에서 티켓의 JPA 엔터티를 만들어 도메인을 미러링합니다. 이것이 애플리케이션이 데이터베이스와 통신하고 티켓 테이블을 생성하는 방법입니다.
package java.boundedContextA.core; import java.boundedContextA.domain.BuyTicket; import java.boundedContextA.ports.in.BuyTicketsAmountCommand; import java.boundedContextA.ports.in.BuyingATicketUseCase; import java.boundedContextA.ports.out.TicketCreatePort; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; import java.util.List; @Service @AllArgsConstructor public class DefaultBuyingATicketUseCase implements BuyingATicketUseCase { final BuyTicket buyTicket; private final List<TicketCreatePort> ticketCreatePorts; @Override public void buyTicket(BuyTicketsAmountCommand buyTicketsAmountCommand) { var ticket = buyTicket.buyTicket(buyTicketsAmountCommand.action(), buyTicketsAmountCommand.start(), buyTicketsAmountCommand.end(), buyTicketsAmountCommand.price(), buyTicketsAmountCommand.owner()); ticketCreatePorts.stream().forEach(ticketCreatedPort -> ticketCreatedPort.createTicket(ticket)); } }
엔티티 저장소를 생성하는 것을 잊지 마세요. 이 저장소는 다른 아키텍처와 마찬가지로 서비스와 통신합니다.
package java.adapters.out.db; import java.boundedContextA.domain.TicketAction; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.annotations.JdbcTypeCode; import java.sql.Types; import java.time.LocalDateTime; import java.util.UUID; @Entity @Table(schema="boundedContextA",name = "boundedContextA.tickets") @Getter @Setter @NoArgsConstructor public class TicketBoughtJpaEntity { @Id @JdbcTypeCode(Types.VARCHAR) private UUID uuid; public TicketBoughtJpaEntity(UUID uuid) { this.uuid = uuid; } @JdbcTypeCode(Types.VARCHAR) private UUID owner; @Column private LocalDateTime start; @Column private LocalDateTime end; @Column private double price; }
"adapters/in" 디렉터리에서 티켓 컨트롤러를 만듭니다. 이 애플리케이션은 외부 소스와 통신합니다.
package java.boundedContextA.adapters.out.db; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; import java.util.UUID; public interface TicketRepository extends JpaRepository<TicketBoughtJpaEntity, UUID> { Optional<TicketBoughtJpaEntity> findByOwner(UUID uuid); }
티켓을 구매했음을 나타내려면 "events" 디렉토리에 이벤트 기록을 생성하세요.
이벤트는 시스템이 다른 시스템 또는 구성 요소와 통신하는 데 중요한 애플리케이션의 중요한 발생을 나타냅니다. 완료된 프로세스, 변경된 상태 또는 추가 조치의 필요성에 대해 외부와 소통하는 또 다른 방법으로 사용됩니다.
package java.boundedContextA.adapters.in; import java.boundedContextA.ports.in.BuyTicketsAmountCommand; import java.boundedContextA.ports.in.BuyingATicketUseCase; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api") public class TicketsController { private final BuyingATicketUseCase buyingATicketUseCase; public TicketsController(BuyingATicketUseCase buyingATicketUseCase) { this.buyingATicketUseCase = buyingATicketUseCase; } @PostMapping("/ticket") public void receiveMoney(@RequestBody BuyTicketsAmountCommand command) { try { buyingATicketUseCase.buyTicket(command); } catch (IllegalArgumentException e) { System.out.println("An IllegalArgumentException occurred: " + e.getMessage()); } } }
모든 것을 한 번에 실행하려면 메인 클래스를 포함하는 것을 잊지 마세요.
package java.boundedContextA.events; import java.time.LocalDateTime; import java.util.UUID; public record TicketIsBoughtEvent(UUID uuid, LocalDateTime start, LocalDateTime end) { }
**이것은 매우 간단한 설명입니다. 더 자세한 코드와 React 인터페이스에 연결하는 방법은 GitHub 저장소(https://github.com/alexiacismaru/techtopia)를 확인하세요.
Java에서 이 아키텍처를 구현하려면 비즈니스 로직과 인터페이스로 깔끔한 핵심 도메인을 정의하고, 외부 시스템과 상호 작용하는 어댑터를 만들고, 코어를 격리된 상태로 유지하면서 모든 것을 함께 작성해야 합니다.
이 아키텍처를 따르면 Java 애플리케이션이 더 잘 구조화되고 유지 관리가 더 쉬우며 향후 변경 사항에 적응할 수 있을 만큼 유연해집니다.
위 내용은 제한된 컨텍스트를 사용하여 Java 애플리케이션 구축의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!