ํด๋น ๊ธ์ ์ ์ฒด ์์ค์ฝ๋๋ github ์์ ํ์ธํ ์ ์์ต๋๋ค. README ๋ฅผ ์ฐธ์กฐํ์ธ์
๋ชฉ์ฐจ
- ๋๊ธฐ
- There is No Magic in spring
๋๊ธฐ
์ต๊ทผ์ DDD ๋ฅผ ํ์ตํ๋ฉด์ ๋๋ฉ์ธ ์ด๋ฒคํธ๋ผ๋ ๊ฒ์ ์๊ฒ ๋์๋ค.
๊ทธ๋ฌ๋ฉด์ Spring ์์ ํ VM ์์์ ์ด๋ฒคํธ๋ฅผ ๋ฐํํ๊ณ ํด๋น ์ด๋ฒคํธ๋ฅผ handling ํ๋ ์ฝ๋๋ฅผ ์ ํ๊ณ , ๊ทธ์ ๊ด๋ จํด์ ์ฌ๋ฌ ์ค์ต์ ํด๋ณด๋ ์ค Spring ์์ ๋ฌด์ธ๊ฐ Magic ์ด ์ผ์ด๋๊ณ ์๋๊ฒ ๊ฐ์ ๋๋์ ๋ฐ์๋ค.
๊ธด ๋ง ํ์ ์์ด ์๋์ ์ฝ๋๋ฅผ ํ์ธํด๋ณด์.
@Entity(name = "orders")
public class Order {
public static Order create() {
return new Order();
}
@Id
private Long id;
private OrderStatus status = CREATED;
public void cancel() {
OrderCanceled event = OrderCanceled.of(id);
Events.raise(event); // event trigger
status = CANCELED;
}
}
์์ ์ฝ๋๋ Order ๋ผ๋ ์ค์ต์ ์ํด ํ์ํ ๊ฐ์์ ๋๋ฉ์ธ์ด๋ค.
์ค์ต์ ์๋๋ฆฌ์ค์์๋ ์์ฃผ ๊ฐ๋จํ ๋ก์ง๋ง์ ๊ฐ์ง๊ณ ์๋ค.
์ฃผ๋ฌธ์ด ์ทจ์๋๋ฉด Membership ์ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒ์ด ๋ค๋ค.
ํด๋น Order ๋ผ๋ ๊ฐ์์ ๋๋ฉ์ธ ์ฝ๋์์ ์ง์คํด์ผ ํ ๋ถ๋ถ์ cancel()
์ด๋ผ๋ ๋ฉ์๋๊ฐ ํ๋ ์ผ์ด๋ค.
Events ๋ผ๋ ์ ํธ๋ฆฌํฐ์ฑ ํด๋์ค์ static ๋ฉ์๋์ธ raise()
๋ฅผ ํธ์ถํด์ ์ด๋ฒคํธ๋ฅผ ๋ฐํํ๋ค.
Events ํด๋์ค๋ฅผ ํ์ธํด๋ณด์
public class Events {
private static ApplicationEventPublisher publisher;
protected static void setPublisher(ApplicationEventPublisher publisher) {
Events.publisher = publisher; // initializing
}
public static void raise(DomainEvent event) {
if (publisher == null) {
throw new IllegalStateException();
}
publisher.publishEvent(event);
}
}
Events ํด๋์ค๋ Spring ์ EventDispatcher ์ธ ApplicationEventPublisher ๋ฅผ ๊ฐ์ธ๊ณ ์๋ ์ผ์ข ์ Facade ์ญํ ์ ์ํํ๋ค.
ํด๋น ์ฝ๋์์๋ raise()
๋ฅผ ํธ์ถํ๊ฒ ๋๋ค๋ฉด ๋ฉค๋ฒ๋ก ๊ฐ์ง๊ณ ์๋ ApplicationEventPublisher
์ ํ๋ ฅํ์ฌ event ๋ฅผ publish ํ๋ค.
ApplicationEventPublisher
๋ ์คํ๋ง์์ ์ ๊ณตํ๋ event publisher ์ธ๋ฐ, Events ๋ผ๋ ํด๋์ค์์๋ ์ฌ์ฉ์ฑ์ ๋์ด๊ธฐ ์ํด์ static ์ผ๋ก EventPublisher ๋ฅผ ์ธ๋ถ์์ ์ฃผ์
๋ฐ๋๋ค.
@Configuration
@RequiredArgsConstructor
public class EventsConfig {
private final ApplicationContext applicationContext;
@Bean
public InitializingBean eventsInitializer() {
return () -> Events.setPublisher(applicationContext);
}
}
Events ๋ฅผ ์ด๊ธฐํ ํ๊ธฐ ์ํด์ InitializingBean ์ ์ฌ์ฉํ๋ค.
์.
์ด๋ฐ ์ ๋ฐ ๊ณผ์ ๋ค์ ํด์ฃผ๊ณ ํด๋น ์ด๋ฒคํธ๋ฅผ ๋ฐ๊ณ ์๋ event handler ๋ก ๊ฐ๋ณด์
@Service
@RequiredArgsConstructor
@Slf4j
public class OrderCanceledEventHandler {
private final MembershipTerminateService membershipTerminateService;
@EventListener(OrderCanceled.class)
public void handle(OrderCanceled event) {
log.info("OrderCanceledEvent occurred !! => {}", event);
membershipTerminateService.terminateBy(event.getOrderId());
}
}
@EventListener
์ด๋
ธํ
์ด์
์ ์ถ๊ฐํ๋ฉด ํด๋น ์ด๋ฒคํธ๊ฐ ๋ฐํ๋ ๋ Listening ํ์ฌ ํน์ ๋ก์ง์ ์ํํ ์ ์๋๋ก ํ๋ค.
์ด๋ ํ ๋ง๋ฒ์ด ์จ์ด์๋ ๊ฒ์ผ๊น?
There is No Magic in spring
๋ ๊ทธ๋ ๋ฏ There is No Magic in spring ์ด๋ค.
๋ด๊ฐ ๊ถ๊ธํ ๊ฒ์ ์ฐ์
- ์ Config ์์
ApplicationContext
๋ฅผ Events ์๊ฒ set ํ๋๊ฐ? @EventListener
๋ ์ด๋ป๊ฒ consuming ํ๋๊ฐ
์ธ๋ฐ, ํ๋์ฉ ํ์ธํด๋ณด์.
์ Config ์์ ApplicationContext ๋ฅผ Events ์๊ฒ set ํ๋๊ฐ?
EventPublisher ๋ฅผ ๋์ ์ผ๋ก set ํด์ฃผ๋ Config ์ฝ๋์์ ApplicationContext
๋ฅผ ๋๊ฒจ์ Events ํด๋์ค์๊ฒ ApplicationEventPublisher
๋ฅผ ์ค์ ํด์ค๋ค.
@Configuration
@RequiredArgsConstructor
public class EventsConfig {
private final ApplicationContext applicationContext;
@Bean
public InitializingBean eventsInitializer() {
return () -> Events.setPublisher(applicationContext);
}
}
Events ๊ฐ ์ํ๋ ๋ฉค๋ฒ๋ ApplicationEventPublisher ์ธ๋ฐ ์ ApplicationContext ๋ฅผ ๋๊ธธ๊น?
public class Events {
private static ApplicationEventPublisher publisher;
protected static void setPublisher(ApplicationEventPublisher publisher) {
Events.publisher = publisher;
}
public static void raise(DomainEvent event) {
//...
}
}
์ฐ๋ฆฌ๊ฐ ์๊ณ ์๋ ApplicationContext
๋ IoC Container ๋ก์ Bean Factory ์ญํ ์ ์ํํ๋ค.
๊ทธ๋ฐ ApplicationContext
์๋ ๋ค์ํ ๊ธฐ๋ฅ์ด ์กด์ฌํ๋๋ฐ, ๊ทธ์ค ํ๋๊ฐ ๋ฐ๋ก ApplicationEventPublishing ์ด๋ค.
์ค์ ์คํ๋ง์ ์ฝ๋๋ฅผ ํ์ธํ๋๋ผ๋ ๋์ผํ๋ค
๊ทธ๋ฆฌ๊ณ ํด๋น ApplicationEventPublisher
์ ๊ตฌํ์ ApplicationContext
๋ฅผ ์์ํ๊ณ ์๋ AbstractApplicationContext
์์ ์กด์ฌํ๋ค.
๊ฒฐ๊ตญ Events
์๊ฒ raise()
๋ผ๋ ํ๋ ฅ์ ์์ฒญํ๊ฒ ๋๋ฉด AbstractApplicationContext
์ ์กด์ฌํ๋ publishEvent()
๊ฐ ํธ์ถ๋๊ฒ ๋๋ค.
Event ๊ฐ Publish ๋๋ฉด Spring ์์ ์ ๊ณตํ๋ ApplicationEvent
ํ์
์ธ์ง ๋จผ์ ํ์ธํ๊ณ ๊ทธ๋ ์ง ์๋ค๋ฉด, Custom ํ Event ๋ผ๋ฉด ApplicationEvent
์ ๊ตฌํ์ฒด์ธ PayloadApplicationEvent
๋ก ๋ณํํ๋ค.
@EventListener
๋ ์ด๋ป๊ฒ consuming ํ๋๊ฐ
์ด์ ์ฌ๊ธฐ์๋ถํฐ @EventListener
๋ ์ด๋ป๊ฒ ๋์ํ๋์ง์ ๋ํ ์ค๋ง๋ฆฌ๊ฐ ๋์จ๋ค.
์๊น ๋ดค๋ AbstractApplicationContext
์์ ์กฐ๊ธ ๋ ๋ด๋ ค๊ฐ๋ค ๋ณด๋ฉด ์ค์ ๋ก Event ๋ฅผ multicast ํ๋ ๊ณณ์ด ๋ฑ์ฅํ๊ฒ ๋๋ค.
ApplicationEventMulticaster
๋ ์ฌ๋ฌ ApplicationListener ๊ฐ์ฒด๋ฅผ ๊ด๋ฆฌํ๊ณ ์ด๋ฒคํธ๋ฅผ publish ํ ์ ์๋ ๋๋ก ํ๋ ์ธํฐํ์ด์ค์ธ๋ฐ,
์ค์ ๋ก ํธ์ถ๋๋ ๊ตฌํ์ฒด๋ ๋ฐ๋ก SimpleApplicationEventMulticaster
์ด๋ค.
๊ทธ๋ฆฌ๊ณ ํด๋น ๊ตฌํ์ฒด์ ๋ด๋ถ์์ @EventListener
์ด๋
ธํ
์ด์
์ด ๋ถ์ ApplicationListener ๋ค์ ๋ชจ๋ ๋ถ๋ฌ์์ ์ ์ ํ Listener ์๊ฒ event ๋ฅผ ์ ๋ฌํ๊ฒ ๋๋ ๊ฒ์ด๋ค.
๋๊ธ