├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src └── main └── java └── net └── michael_j └── ddd ├── EventLightApplication.java ├── application ├── listener │ ├── OrderListener.java │ ├── UserLogoutListener.java │ ├── UserRegisterListener.java │ └── UserStatisticCounter.java └── service │ ├── OrderService.java │ └── UserService.java ├── core ├── event │ ├── DomainEvent.java │ ├── DomainEventPublisher.java │ └── GuavaDomainEventPublisher.java ├── order │ ├── Order.java │ └── OrderCreatedEvent.java ├── shared │ ├── DomainEvent.java │ ├── DomainEventPublisher.java │ └── DomainEventSubscriber.java └── user │ ├── User.java │ └── event │ ├── LogoutEvent.java │ ├── UserEvent.java │ ├── UserEventPublisher.java │ └── UserEventType.java ├── infrastructure └── email │ └── EmailSender.java └── web ├── OrderController.java └── UserController.java /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by .ignore support plugin (hsz.mobi) 3 | ### Java template 4 | *.class 5 | 6 | # Mobile Tools for Java (J2ME) 7 | .mtj.tmp/ 8 | 9 | # Package Files # 10 | *.jar 11 | *.war 12 | *.ear 13 | 14 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 15 | hs_err_pid* 16 | 17 | ### JetBrains template 18 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 19 | 20 | *.iml 21 | 22 | ## Directory-based project format: 23 | .idea/ 24 | 25 | # if you remove the above rule, at least ignore the following: 26 | 27 | # User-specific stuff: 28 | # .idea/workspace.xml 29 | # .idea/tasks.xml 30 | # .idea/dictionaries 31 | 32 | # Sensitive or high-churn files: 33 | # .idea/dataSources.ids 34 | # .idea/dataSources.xml 35 | # .idea/sqlDataSources.xml 36 | # .idea/dynamic.xml 37 | # .idea/uiDesigner.xml 38 | 39 | # Gradle: 40 | # .idea/gradle.xml 41 | # .idea/libraries 42 | 43 | # Mongo Explorer plugin: 44 | # .idea/mongoSettings.xml 45 | 46 | ## File-based project format: 47 | *.ipr 48 | *.iws 49 | 50 | ## Plugin-specific files: 51 | 52 | # IntelliJ 53 | /out/ 54 | 55 | # mpeltonen/sbt-idea plugin 56 | .idea_modules/ 57 | 58 | # JIRA plugin 59 | atlassian-ide-plugin.xml 60 | 61 | # Crashlytics plugin (for Android Studio and IntelliJ) 62 | com_crashlytics_export_strings.xml 63 | crashlytics.properties 64 | crashlytics-build.properties 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Michael Jiang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # event-light 2 | 3 | > 领域事件的实践DEMO 4 | 5 | #### 更新日志: 6 | 7 | 2017-08-29 8 | 9 | 补充了最新的领域事件实现方式,以下单为例 10 | 11 | #### DEMO简介 12 | 13 | 本DEMO以常用的用户注册\登录\登出为例,介绍领域事件的应用以及几种实现方式的不同. 14 | 15 | #### 运行方法 16 | 17 | > git clone https://github.com/mymonkey110/event-light.git 18 | > cd event-light 19 | > mvn spring-boot:run 20 | 21 | #### 工程结构 22 | 23 | * 核心层 包含用户和领域事件 24 | * 基础设施层 包括Email发送的示例 25 | * 应用层 监听用户注册\登出事件,统计登录次数 26 | * WEB层 用户UI层 27 | 28 | #### 详细分析 29 | 30 | * 用户注册(异步发送领域事件) 31 | 32 | 在浏览器访问: 模拟用户注册 33 | 34 | 观察日志发现,注册统计和发送EMAIL的执行与处理request的执行线程不同,说明异步发送是成功的. 35 | 36 | * 用户登录(同步发送领域事件) 37 | 38 | 在浏览器访问: 模拟用户登录 39 | 40 | 观察日志发现,登录统计的执行和request的处理在同一个线程中,说明同步发送是成功的. 41 | 42 | * 用户登出(Spring同步发送领域事件) 43 | 44 | 与上面两种事件处理机制不同,用户登出事件利用了Spring自带的事件机制. 45 | 46 | 在浏览器访问: 模拟用户登出 47 | 48 | 观察日志发现,用户登出监听器和request的处理在同一个线程中,说明Spring自带的是同步的. 49 | 50 | 设计初衷见我的博客: 51 | [实现领域事件](http://michael-j.net/2016/01/19/实现领域事件/) 52 | [再谈领域事件](http://michael-j.net/2017/08/13/再谈领域事件/) 53 | 54 | 55 | #### MIT LICENSE 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.haocai.event 7 | event-light 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | event-light 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.6.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | 1.7 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-aop 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-web 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-test 39 | test 40 | 41 | 42 | 43 | com.google.guava 44 | guava 45 | 18.0 46 | 47 | 48 | 49 | 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-maven-plugin 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/EventLightApplication.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class EventLightApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(EventLightApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/application/listener/OrderListener.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.application.listener; 2 | 3 | import net.michael_j.ddd.core.order.OrderCreatedEvent; 4 | import net.michael_j.ddd.core.shared.DomainEventPublisher; 5 | import net.michael_j.ddd.core.shared.DomainEventSubscriber; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.stereotype.Service; 9 | 10 | import javax.annotation.PostConstruct; 11 | 12 | @Service 13 | public class OrderListener implements DomainEventSubscriber { 14 | 15 | private static Logger LOG = LoggerFactory.getLogger(OrderListener.class); 16 | 17 | @PostConstruct 18 | public void init() { 19 | DomainEventPublisher.subscribe(OrderCreatedEvent.class, this); 20 | } 21 | 22 | @Override 23 | public void handle(OrderCreatedEvent event) { 24 | LOG.info("received order created event:{}.", event); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/application/listener/UserLogoutListener.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.application.listener; 2 | 3 | import net.michael_j.ddd.core.user.event.LogoutEvent; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.context.ApplicationListener; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | * Created by Michael Jiang on 16/1/13. 11 | */ 12 | @Service 13 | public class UserLogoutListener implements ApplicationListener { 14 | 15 | private static Logger LOG = LoggerFactory.getLogger(UserLogoutListener.class); 16 | 17 | @Override 18 | public void onApplicationEvent(LogoutEvent event) { 19 | LOG.info("user:{} logout!", event.getName()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/application/listener/UserRegisterListener.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.application.listener; 2 | 3 | import com.google.common.eventbus.Subscribe; 4 | import net.michael_j.ddd.core.user.event.UserEvent; 5 | import net.michael_j.ddd.core.user.event.UserEventPublisher; 6 | import net.michael_j.ddd.core.user.event.UserEventType; 7 | import net.michael_j.ddd.infrastructure.email.EmailSender; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | 11 | import javax.annotation.Resource; 12 | 13 | /** 14 | * Created by Michael Jiang on 16/1/12. 15 | */ 16 | @Service 17 | public class UserRegisterListener { 18 | @Resource 19 | private EmailSender emailSender; 20 | 21 | @Autowired 22 | public UserRegisterListener(UserEventPublisher userEventPublisher) { 23 | userEventPublisher.register(this); 24 | } 25 | 26 | @Subscribe 27 | public void listenUserRegisterEvent(UserEvent userEvent) { 28 | if (userEvent.getType().equals(UserEventType.REGISTER)) { 29 | emailSender.send(userEvent.getName(), "welcome to join us!"); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/application/listener/UserStatisticCounter.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.application.listener; 2 | 3 | import com.google.common.eventbus.AllowConcurrentEvents; 4 | import com.google.common.eventbus.Subscribe; 5 | import net.michael_j.ddd.core.user.event.UserEvent; 6 | import net.michael_j.ddd.core.user.event.UserEventPublisher; 7 | import net.michael_j.ddd.core.user.event.UserEventType; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.util.concurrent.atomic.AtomicLong; 14 | 15 | /** 16 | * Created by Michael Jiang on 16/1/12. 17 | */ 18 | @Service 19 | public class UserStatisticCounter { 20 | 21 | private AtomicLong registerCounter = new AtomicLong(0L); 22 | private AtomicLong loginCounter = new AtomicLong(0L); 23 | 24 | private static Logger LOG = LoggerFactory.getLogger(UserStatisticCounter.class); 25 | 26 | @Autowired 27 | public UserStatisticCounter(UserEventPublisher userEventPublisher) { 28 | userEventPublisher.register(this); 29 | } 30 | 31 | @Subscribe 32 | @AllowConcurrentEvents 33 | public void listenUserEvent(UserEvent userEvent) { 34 | if (userEvent.getType().equals(UserEventType.REGISTER)) { 35 | LOG.info("add a new user,name:{},age:{}. total register user:{}.", 36 | userEvent.getName(), userEvent.getAge(), registerCounter.incrementAndGet()); 37 | } else if (userEvent.getType().equals(UserEventType.LOGIN)) { 38 | LOG.info("user:{} login,all user total login times:{}.", userEvent.getName(), loginCounter.incrementAndGet()); 39 | } else { 40 | throw new IllegalStateException("unknown user event type"); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/application/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.application.service; 2 | 3 | import net.michael_j.ddd.core.order.OrderCreatedEvent; 4 | import net.michael_j.ddd.core.shared.DomainEventPublisher; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | public class OrderService { 11 | 12 | private static Logger LOG = LoggerFactory.getLogger(OrderService.class); 13 | 14 | public void createOrder(long uid, String name, String content) { 15 | LOG.info("user:{} create order:{}-{}.", uid, name, content); 16 | DomainEventPublisher.publish(new OrderCreatedEvent(name, content)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/application/service/UserService.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.application.service; 2 | 3 | import net.michael_j.ddd.core.user.event.LogoutEvent; 4 | import net.michael_j.ddd.core.user.event.UserEvent; 5 | import net.michael_j.ddd.core.user.event.UserEventPublisher; 6 | import net.michael_j.ddd.core.user.event.UserEventType; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.BeansException; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.context.ApplicationContext; 12 | import org.springframework.context.ApplicationContextAware; 13 | import org.springframework.stereotype.Service; 14 | 15 | /** 16 | * Created by Michael Jiang on 16/1/12. 17 | */ 18 | @Service 19 | public class UserService implements ApplicationContextAware { 20 | private final UserEventPublisher userEventPublisher; 21 | 22 | private ApplicationContext applicationContext; 23 | 24 | private static Logger LOG = LoggerFactory.getLogger(UserService.class); 25 | 26 | @Autowired 27 | public UserService(UserEventPublisher userEventPublisher) { 28 | this.userEventPublisher = userEventPublisher; 29 | } 30 | 31 | public void register(String name, int age) { 32 | LOG.info("user:{} age:{} register done.", name, age); 33 | userEventPublisher.asyncPublish(new UserEvent(name, age, UserEventType.REGISTER)); 34 | } 35 | 36 | public void login(String name) { 37 | LOG.info("user:{} login!", name); 38 | userEventPublisher.publish(new UserEvent(name, UserEventType.LOGIN)); 39 | } 40 | 41 | public void logout(String name) { 42 | LOG.info("user:{} logout!", name); 43 | applicationContext.publishEvent(new LogoutEvent("user_service", name)); 44 | } 45 | 46 | @Override 47 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 48 | this.applicationContext = applicationContext; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/core/event/DomainEvent.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.core.event; 2 | 3 | import java.util.Date; 4 | 5 | /** 6 | * Created by Michael Jiang on 16/1/12. 7 | */ 8 | public abstract class DomainEvent { 9 | private Date occurredTime; 10 | 11 | protected abstract String identify(); 12 | 13 | public DomainEvent() { 14 | occurredTime =new Date(); 15 | } 16 | 17 | public Date getOccurredTime() { 18 | return occurredTime; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/core/event/DomainEventPublisher.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.core.event; 2 | 3 | /** 4 | * 领域事件发射器 5 | * Created by Michael Jiang on 16/1/12. 6 | */ 7 | public interface DomainEventPublisher { 8 | String identify(); 9 | 10 | void register(Object listener); 11 | 12 | void publish(T event); 13 | 14 | void asyncPublish(T event); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/core/event/GuavaDomainEventPublisher.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.core.event; 2 | 3 | import com.google.common.eventbus.AsyncEventBus; 4 | import com.google.common.eventbus.EventBus; 5 | 6 | import java.util.concurrent.Executors; 7 | 8 | 9 | /** 10 | * Guava事件发布器实现 11 | * Created by Michael Jiang on 16/1/12. 12 | */ 13 | public abstract class GuavaDomainEventPublisher implements DomainEventPublisher { 14 | private EventBus syncBus = new EventBus(identify()); 15 | private EventBus asyncBus = new AsyncEventBus(identify(), Executors.newFixedThreadPool(1)); 16 | 17 | @Override 18 | public void register(Object listener) { 19 | syncBus.register(listener); 20 | asyncBus.register(listener); 21 | } 22 | 23 | @Override 24 | public void publish(DomainEvent event) { 25 | syncBus.post(event); 26 | } 27 | 28 | @Override 29 | public void asyncPublish(DomainEvent event) { 30 | asyncBus.post(event); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/core/order/Order.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.core.order; 2 | 3 | public class Order { 4 | private Status status; 5 | private String name; 6 | private String content; 7 | 8 | public Order(String name, String content) { 9 | this.status = Status.PENDING; 10 | this.name = name; 11 | this.content = content; 12 | } 13 | 14 | public Status getStatus() { 15 | return status; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public String getContent() { 23 | return content; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "Order{" + 29 | "status=" + status + 30 | ", name='" + name + '\'' + 31 | ", content='" + content + '\'' + 32 | '}'; 33 | } 34 | 35 | enum Status { 36 | PENDING, PAID, CANCELLED 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/core/order/OrderCreatedEvent.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.core.order; 2 | 3 | import net.michael_j.ddd.core.shared.DomainEvent; 4 | 5 | import java.util.Date; 6 | 7 | public class OrderCreatedEvent implements DomainEvent { 8 | private String name; 9 | private String content; 10 | 11 | public OrderCreatedEvent(String name, String content) { 12 | this.name = name; 13 | this.content = content; 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | public String getContent() { 21 | return content; 22 | } 23 | 24 | @Override 25 | public Date occurredTime() { 26 | return new Date(); 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "OrderCreatedEvent{" + 32 | "name='" + name + '\'' + 33 | ", content='" + content + '\'' + 34 | '}'; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/core/shared/DomainEvent.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.core.shared; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | /** 7 | * 领域事件 8 | * Created by Michael Jiang on 16-11-17. 9 | */ 10 | public interface DomainEvent extends Serializable { 11 | Date occurredTime(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/core/shared/DomainEventPublisher.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.core.shared; 2 | 3 | import com.google.common.collect.Lists; 4 | 5 | import java.util.List; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | /** 9 | * 领域事件发布器 10 | * Created by Michael Jiang on 16-11-17. 11 | */ 12 | public class DomainEventPublisher { 13 | private static ConcurrentHashMap, List>> subscriberMap 14 | = new ConcurrentHashMap<>(); 15 | 16 | 17 | public synchronized static void subscribe(Class domainEventClazz, DomainEventSubscriber subscriber) { 18 | List> domainEventSubscribers = subscriberMap.get(domainEventClazz); 19 | if (domainEventSubscribers == null) { 20 | domainEventSubscribers = Lists.newArrayList(); 21 | } 22 | domainEventSubscribers.add(subscriber); 23 | subscriberMap.put(domainEventClazz, domainEventSubscribers); 24 | } 25 | 26 | @SuppressWarnings("unchecked") 27 | public static void publish(final T domainEvent) { 28 | if (domainEvent == null) { 29 | throw new IllegalArgumentException("domain event is null"); 30 | } 31 | List> subscribers = subscriberMap.get(domainEvent.getClass()); 32 | if (subscribers != null && !subscribers.isEmpty()) { 33 | for (DomainEventSubscriber subscriber : subscribers) { 34 | subscriber.handle(domainEvent); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/core/shared/DomainEventSubscriber.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.core.shared; 2 | 3 | /** 4 | * 领域事件订阅器 5 | * Created by Michael Jiang on 16-11-17. 6 | */ 7 | public interface DomainEventSubscriber { 8 | 9 | /** 10 | * 订阅者处理事件 11 | * 12 | * @param event 领域事件 13 | */ 14 | void handle(T event); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/core/user/User.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.core.user; 2 | 3 | /** 4 | * Created by Michael Jiang on 16/1/12. 5 | */ 6 | public class User { 7 | private String name; 8 | private int age; 9 | 10 | public User(String name, int age) { 11 | setName(name); 12 | setAge(age); 13 | } 14 | 15 | public String getName() { 16 | return name; 17 | } 18 | 19 | public void setName(String name) { 20 | this.name = name; 21 | } 22 | 23 | public int getAge() { 24 | return age; 25 | } 26 | 27 | public void setAge(int age) { 28 | this.age = age; 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return "User{" + 34 | "name='" + name + '\'' + 35 | ", age=" + age + 36 | '}'; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/core/user/event/LogoutEvent.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.core.user.event; 2 | 3 | import org.springframework.context.ApplicationEvent; 4 | 5 | /** 6 | * user logout event 7 | * Created by Michael Jiang on 16/1/13. 8 | */ 9 | public class LogoutEvent extends ApplicationEvent { 10 | private String name; 11 | 12 | public LogoutEvent(Object source, String name) { 13 | super(source); 14 | this.name = name; 15 | } 16 | 17 | public String getName() { 18 | return name; 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return "LogoutEvent{" + 24 | "name='" + name + '\'' + 25 | '}'; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/core/user/event/UserEvent.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.core.user.event; 2 | 3 | import net.michael_j.ddd.core.event.DomainEvent; 4 | 5 | /** 6 | * user event 7 | * Created by Michael Jiang on 16/1/12. 8 | */ 9 | public class UserEvent extends DomainEvent { 10 | private String name; 11 | private int age; 12 | private UserEventType type; 13 | 14 | public UserEvent(String name, UserEventType type) { 15 | this.name = name; 16 | this.type = type; 17 | } 18 | 19 | public UserEvent(String name, int age, UserEventType type) { 20 | this.name = name; 21 | this.age = age; 22 | this.type = type; 23 | } 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | 29 | public int getAge() { 30 | return age; 31 | } 32 | 33 | public UserEventType getType() { 34 | return type; 35 | } 36 | 37 | @Override 38 | protected String identify() { 39 | return "user_event"; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "UserEvent{" + 45 | "name='" + name + '\'' + 46 | ", age=" + age + 47 | ", type=" + type + 48 | '}'; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/core/user/event/UserEventPublisher.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.core.user.event; 2 | 3 | import net.michael_j.ddd.core.event.GuavaDomainEventPublisher; 4 | import org.springframework.stereotype.Service; 5 | 6 | /** 7 | * 用户事件发布器 8 | * Created by Michael Jiang on 16/1/12. 9 | */ 10 | @Service 11 | public class UserEventPublisher extends GuavaDomainEventPublisher { 12 | @Override 13 | public String identify() { 14 | return "user_event_publisher"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/core/user/event/UserEventType.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.core.user.event; 2 | 3 | /** 4 | * Created by Michael Jiang on 16/1/12. 5 | */ 6 | public enum UserEventType { 7 | REGISTER, 8 | LOGIN 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/infrastructure/email/EmailSender.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.infrastructure.email; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * Created by Michael Jiang on 16/1/12. 9 | */ 10 | @Component 11 | public class EmailSender { 12 | private static Logger LOG = LoggerFactory.getLogger(EmailSender.class); 13 | 14 | public void send(String name, String content) { 15 | LOG.info("sending email to {},content:{}.", name, content); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/web/OrderController.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.web; 2 | 3 | import net.michael_j.ddd.application.service.OrderService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.PostMapping; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | @RestController 10 | @RequestMapping("/order") 11 | public class OrderController { 12 | 13 | private final OrderService orderService; 14 | 15 | @Autowired 16 | public OrderController(OrderService orderService) { 17 | this.orderService = orderService; 18 | } 19 | 20 | @PostMapping("/create.do") 21 | public String createOrder(long uid, String name, String content) { 22 | orderService.createOrder(uid, name, content); 23 | return "order create completed!"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/michael_j/ddd/web/UserController.java: -------------------------------------------------------------------------------- 1 | package net.michael_j.ddd.web; 2 | 3 | import net.michael_j.ddd.application.service.UserService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestMethod; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | /** 10 | * Created by Michael Jiang on 16/1/12. 11 | */ 12 | @RestController 13 | @RequestMapping("/user") 14 | public class UserController { 15 | @Autowired 16 | private UserService userService; 17 | 18 | @RequestMapping(value = "/register.do", method = RequestMethod.GET) 19 | public String register(String name, Integer age) { 20 | userService.register(name, age); 21 | return "register completed!"; 22 | } 23 | 24 | @RequestMapping(value = "/login.do", method = RequestMethod.GET) 25 | public String login(String name) { 26 | userService.login(name); 27 | return "login completed!"; 28 | } 29 | 30 | @RequestMapping(value = "/logout.do", method = RequestMethod.GET) 31 | public String logout(String name) { 32 | userService.logout(name); 33 | return "logout completed!"; 34 | } 35 | } 36 | --------------------------------------------------------------------------------