1. IoC
오늘은 '제어의 역전(inversion of control)'에 대하여 한번 알아보겠다.
- Order Entity 에서 제어를 했다.
- 객체가 자신이 사용할 객체를 스스로 선택하지 않고, 스스로 생성하지도 않는다.
-> 생성할 때 전달(주입)을 받게 된다. - 서블릿, 스프링 같은 프레임워크에서는 제어의 권한이 프레임워크에 있다.
- 프레임워크는 전체 흐름의 제어 권한을 가지고 있다.
- 애플리케이션 코드가 프레임워크에 짜놓은 틀에서 수동적으로 동작하게 된다. => Hollywood Principle
학습 목표
- OrdeContext : 애플리케이션의 주요 객체에 대해서 생성과 관계설정
- OrderService : Order에 대한 비즈니스 로직
OrderService는 Voucher 서비스와 Order에 대한 정보를 기록하고, 조회할 수 있는 repository에 대해서 의존성을 가진다.
코드를 보려면 아래의 [더보기]를 누른다.
public class OrderService {
private final VoucherService voucherService;
private final OrderRepository orderRepository;
public OrderService(VoucherService voucherService,OrderRepository orderRepository) {
this.voucherService = voucherService;
this.orderRepository = orderRepository;
}
// Order 생성에 대한 책임을 갖게 된다.
// voucher 없는 경우
public Order createOrder(UUID customerId, List<OrderItem> orderItems) {
var order = new Order(UUID.ramdomUUID(), customerId, orderItems);
orderRepository.insert(order);
return order;
}
// Order 생성에 대한 책임을 갖게 된다.
// voucher 있는 경우
public Order createOrder(UUID customerId, List<OrderItem> orderItems, UUID voucherID) {
var voucher = voucherService.getVoucher(voucherID);
var order = new Order(UUID.ramdomUUID(), customerId, orderItems, voucher);
// order 기록을 다시 꺼낼 수 있기 때문에 저장한다. order의 연속성을 보장한다.
orderRepository.insert(order);
voucherService.useVoucher(voucher); // 주문을 한다면 바우처를 통해서 하게 된다.
return order;
}
}
public interface OrderRepository {
public void insert(Order order) {
}
}
// Service와 Repository에 대한 책임을 담당한다. 의존 관계
// 각각 컴포넌트를 생성하는 메소드를 만들어보자.
public class OrderContext {
VoucherRepository voucherRepository() {
return new VoucherRepository() {
@Override
public Optinal<Voucher> findById(UUID voucher) {
return Optinal.empty();
}
};
}
OrderRepository orderRepository() {
return new orderRepository() {
@Override
public void insert(Order order){
return Optinal.empty();
}
};
}
public VoucherService() {
return new VoucherService(voucherRepository());
}
public OrderService orderService() {
return new OrderService(VoucherService());
}
}
// 바우처에 대한 정보를 어디에서 불러와서 클래스를 생성해양 한다.
public class VoucherService {
// 바우처도 repository가 필요하다.
private final VoucherRepository voucherRepository;
public VoucherService(VoucherRepository voucherRepository) {
this.voucherRepository = voucherRepository;
}
public Voucher getVoucher(UUID voucherID) { // OrderService에서 이용
return voucherRepository
.findById(voucherID)
.orElseThrow(() -> new RuntimeException("Can not find a voucher for " + voucherID));
}
public void useVoucher(Voucher voucher) {
}
}
// 어떻게 보관하는지에 관해서 계속 바뀜 -> interface
public interface VoucherRepository {
// Optional<Voucher> : Entity에 대한 정보가 없을 수도 있다.
// 관계에 대한 설정을 context에서 가져간다.
Optional<Voucher> Voucher findById(UUID voucherID);
}
// OrderTester.java
public class OrderTester {
public static void main(String[] args) {
var customerId = UUID.randomUUID();
var orderItems = new ArrayList<OrderItem>() {{
add(new OrderItem(UUID.random(), 100L, 1));
}};
var orderContext = new OrderContext();
var orderService = orderContext.orderService();
// order를 만들 때, voucher가 있을 수도 있고 없을 수도 있다.
var order = orderService.createOrder(customerId, orderItems);
}
}
지금까지 짠 코드를 시각화 해보면...
- OrderContext는 주문에 대한 전반적인 도메인 객체에 대한 생성을 책임지고 있다.
- OrderService는 자신이 직접 OrderRepository를 선택하지 않는다.
- VoucherService 또한 직접 생성하지 않는다. - 객체를 만드는 제어권을 OrderContext에게 넘겼다.
- IoC Container : IoC가 일어나는 공간
- IoC Container에서 개별 객체들의 의존 관계 설정이 이뤄지게 된다.
- 객체의 생성과 파괴를 담당한다.
- (OrderSerive - OrderRepository), (OrderService - VoucherService) 관계 정의
- 객체지향 프로그램을 할 때, 느슨한 결합도를 만드는 것이 중요하다!
객체가 자신이 사용하는 객체를 직접 만들지 않으면 된다.
객체 생성에 대한 권한을 위임하자.
2. DDD
Repository와 Service가 무엇일까?
Aggregate
: 일종의 Entity.
- Entity들의 집합
- 각각의 Aggregate는 root가 존재한다. => root는 하나의 Entity다.
- Aggregate => ACID(Atomic, Constant, Isolate, Durable) 트랜잭션이 만들어진다.
- 상태가 변환이 되는 것이 Commit 단위가 된다. - 서비스에서 이런 형태의 트랜잭션을 한다.
- 서비스는 상태가 없다. 메소드만 있다.
* Repository에서 하는 일...
- Order를 실제로 저장하고, (상태가 복구가 된다.)
- Order의 상태를 불러오고,
- OrderService도 Domain model Layer에 존재한다.
- OrderService -> Repository를 이용해서 실제 Order 트랜잭션
Repository는 Entity를 저장하는 저장소다.
3. ApplicationContext
Application Context는 일종의 ioc Container이다.
order Context에서 객체에 대한 생성과 조합이 이루어졌다.
- 개별 객체들의 의존관계 설정이 자동으로 이루어진다.
- 객체들의 생성과 파괴 조합 등을 관장한다.
- register : 필요한 의존관계를 맺어줌
- 생명주기를 Managing하면서 객체에 대한 인스턴스를 만들어준다. - IoC Container를 스프링에서 Application Context라는 Interface를 통해서 제공한다.
- Bean Factory를 상속한다.
Bean
: IoC Container에서 관리되어지는 객체
스프링에서 제공하는 Application Context, BeanFactory, IoC Container에 의해 관리되어지는 객체를 말한다.
- 객체가 ioc에서 관리되는 객체와 아닌 객체를 분리하기 위해서 Bean을 만들었다.
- 어노테이션 기반으로 Bean을 정의한다.
Configuration Metadata
- 스프링의 ApplicaitonContext는 실제 만들어야할 Bean 정보를 Configuration Metadata로 받아옴
- 어플리케이션에서 객체들을 도식화 한 것이다. 도면
- xml 기반으로 작성하거나 java file 기반으로 작성할 수 있다.
- xml : GenericXmlApplicationContext 구현체 사용
- java : AnnotationConfigApplicationContext 구현체 사용
- 공식문서에 따르면 최근에는 자바 기반
OrderContext => AppConfiguration
// AppConfiguration.java
// OrderContext to AppConfiguration
// Bean을 정의한 도면이라는 것을 알려줘야 한다.
@Configuration
public class AppConfiguration {
@Bean
VoucherRepository voucherRepository() {
return new VoucherRepository() {
@Override
public Optinal<Voucher> findById(UUID voucher) {
return Optinal.empty();
}
};
}
@Bean
OrderRepository orderRepository() {
return new orderRepository() {
@Override
public void insert(Order order){
return Optinal.empty();
}
};
}
@Bean
public VoucherService() {
return new VoucherService(voucherRepository());
}
@Bean
public OrderService orderService() {
return new OrderService(VoucherService());
}
}
@Bean을 정의한 Configuration 메타 데이터가 된다.
// OrderTester.java
public class OrderTester {
public static void main(String []args) {
var applicationContext = new AnnotationConfigapplicationContext(AppConfiguration.class);
var customerId = UUID.randomUUID();
var orderService = applicationContext.getBean(orderService.class);
var orderItems = new ArrayList<orderItem>() {{
add(new OrderItem(UUID.randomUUID(), 100L, 1));
}};
var order = orderService.createOrder(customerId, );
Assert.isTrue(order.totalAmount() == 100L, MessageFormat.format("{0} is not 100L", order.totalAmount()));
}
}
실행해보면 여러 로그가 찍힌다.
싱글톤 Bean이 된다.
4. Dependency Injection
IoC를 이야기할 때 꼭 Dependency Injection이라는 용어가 나온다.
- IoC를 구현하는 패턴이다.
- 전략 패턴
- 서비스 로케이터 패턴
- 팩토리 패턴
- 의존관계 주입패턴
생성자를 통해서 주입을 받는 패턴을 생성자 주입 패턴이라고 한다.
스프링은 Constructor-based 외에 setter-based 도 제공한다.
Dependency Resolution
자세한 설명은 아래의 공식문서 사이트에서 읽어보자.
Core Technologies
In the preceding scenario, using @Autowired works well and provides the desired modularity, but determining exactly where the autowired bean definitions are declared is still somewhat ambiguous. For example, as a developer looking at ServiceConfig, how do
docs.spring.io
'2023 활동 - 4학년 > [1월 ~ 4월] sw 아카데미 백엔드 과정' 카테고리의 다른 글
[2023.02.27 / CNU SW 아카데미] 39일차 회고록 (0) | 2023.02.27 |
---|---|
[2023.02.24 / CNU SW 아카데미] 38회차 회고록 (2) | 2023.02.26 |
[2023.02.23 / CNU SW 아카데미] 37회차 회고록 (0) | 2023.02.23 |
[2023.02.22 / CNU SW 아카데미] 36회차 회고록 (0) | 2023.02.22 |
[2023.02.21 / CNU SW 아카데미] 35일차 회고록 (0) | 2023.02.21 |