Skip to content

Commit

Permalink
Merge pull request #46 from dnd-side-project/feat/ddd
Browse files Browse the repository at this point in the history
이벤트 처리
  • Loading branch information
haeyonghahn authored Feb 3, 2024
2 parents 8e709ba + f2fc624 commit bd2aedd
Show file tree
Hide file tree
Showing 14 changed files with 262 additions and 63 deletions.
10 changes: 6 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation group: 'commons-codec', name: 'commons-codec', version: '1.9'
implementation 'com.google.code.gson:gson:2.10.1'
implementation group: 'net.jodah', name: 'typetools', version: '0.4.2'

developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
// DB
Expand Down Expand Up @@ -100,9 +102,9 @@ tasks.jacocoTestReport {
classDirectories.setFrom(
files(classDirectories.files.collect {
fileTree(dir: it,
exclude: [
"*.test.*"
] + Qdomains)
exclude: [
"*.test.*"
] + Qdomains)
})
)
}
Expand All @@ -121,7 +123,7 @@ tasks.jacocoTestCoverageVerification {
enabled = true // 활성화
element = 'METHOD' // 메소드 단위로 커버리지 체크
excludes = [
"*.test.*"
"*.test.*"
] + Qdomains // 제외할 Qdomains 패턴 추가
}

Expand Down
9 changes: 9 additions & 0 deletions db/migration/V0__init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,13 @@ CREATE TABLE IF NOT EXISTS `image` (
`list_idx` int(11) DEFAULT NULL,
PRIMARY KEY (`image_id`),
FOREIGN KEY (`record_number`) REFERENCES `record` (`record_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `evententry` (
`id` int NOT NULL AUTO_INCREMENT,
`type` varchar(255) DEFAULT NULL,
`content_type` varchar(255) DEFAULT NULL,
`payload` mediumtext DEFAULT NULL,
`timestamp` datetime NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
14 changes: 14 additions & 0 deletions src/main/java/com/dnd/gooding/common/event/Event.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.dnd.gooding.common.event;

public abstract class Event {

private long timestamp;

public Event() {
this.timestamp = System.currentTimeMillis();
}

public long getTimestamp() {
return timestamp;
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/dnd/gooding/common/event/EventHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.dnd.gooding.common.event;

import net.jodah.typetools.TypeResolver;

public interface EventHandler<T> {

void handle(T event);

/**
* 핸들러가 이벤트를 처리할 수 있는지 여부를 검사
*
* @param event
* @return
*/
default boolean canHandle(Object event) {
Class<?>[] typeArgs = TypeResolver.resolveRawArguments(EventHandler.class, this.getClass());
return typeArgs[0].isAssignableFrom(event.getClass());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.dnd.gooding.common.event;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Order(0)
@Component
public class EventResetProcessor {

private ThreadLocal<Integer> nestedCount =
new ThreadLocal<Integer>() {

@Override
protected Integer initialValue() {
return 0;
}
};

@Around("@within(org.springframework.stereotype.Service)")
public Object doReset(ProceedingJoinPoint joinPoint) throws Throwable {
nestedCount.set(nestedCount.get() + 1);
try {
return joinPoint.proceed();
} finally {
nestedCount.set(nestedCount.get() - 1);
if (nestedCount.get() == 0) {
Events.reset();
}
}
}
}
57 changes: 57 additions & 0 deletions src/main/java/com/dnd/gooding/common/event/Events.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.dnd.gooding.common.event;

import java.util.ArrayList;
import java.util.List;

public class Events {

private static ThreadLocal<List<EventHandler<?>>> handlers = new ThreadLocal<>();
private static ThreadLocal<Boolean> publishing =
new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return Boolean.FALSE;
}
};

public static void raise(Object event) {
if (publishing.get()) {
return;
}

try {
publishing.set(Boolean.TRUE);

List<EventHandler<?>> eventHandlers = handlers.get();
if (eventHandlers == null) {
return;
}
for (EventHandler handler : eventHandlers) {
if (handler.canHandle(event)) {
handler.handle(event);
}
}
} finally {
publishing.set(Boolean.FALSE);
}
}

public static void handle(EventHandler<?> handler) {
if (publishing.get()) {
return;
}

List<EventHandler<?>> eventHandlers = handlers.get();
if (eventHandlers == null) {
eventHandlers = new ArrayList<>();
handlers.set(eventHandlers);
}
eventHandlers.add(handler);
}

public static void reset() {
if (!publishing.get()) {
handlers.remove();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.dnd.gooding.oauth.command.application;

import com.dnd.gooding.common.event.Events;
import com.dnd.gooding.oauth.command.domain.ExternalLogin;
import com.dnd.gooding.oauth.command.domain.OAuth;
import com.dnd.gooding.oauth.command.domain.OAuthId;
import com.dnd.gooding.oauth.command.domain.OAuthRepository;
import com.dnd.gooding.oauth.command.model.OAuthMember;
import com.dnd.gooding.oauth.infra.MemberCreatedHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -15,19 +18,25 @@ public class CreateOAuthService {

private ExternalLogin externalLogin;

@Autowired private MemberCreatedHandler memberCreatedHandler;

public CreateOAuthService(OAuthRepository oAuthRepository, ExternalLogin externalLogin) {
this.oAuthRepository = oAuthRepository;
this.externalLogin = externalLogin;
}

public OAuth createOAuth(String code) {
@Transactional
public OAuth create(String code) {
OAuthMember oAuthMember = externalLogin.getOauthToken(code);
Events.handle(memberCreatedHandler);

OAuth oAuth =
new OAuth(
new OAuthId(oAuthMember.getoAuthId()),
oAuthMember.getImageUrl(),
oAuthMember.getProvider(),
oAuthMember.getEmail());

createOAuth(oAuth);
return oAuth;
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/dnd/gooding/oauth/command/domain/OAuth.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.dnd.gooding.oauth.command.domain;

import com.dnd.gooding.common.event.Events;
import com.dnd.gooding.user.command.domain.MemberCreatedEvent;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;
Expand All @@ -20,6 +22,7 @@ public OAuth(OAuthId oAuthId, String imageUrl, String provider, String email) {
this.imageUrl = imageUrl;
this.provider = provider;
this.email = email;
Events.raise(new MemberCreatedEvent(email, oAuthId));
}

public OAuthId getoAuthId() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.dnd.gooding.oauth.infra;

import com.dnd.gooding.common.event.EventHandler;
import com.dnd.gooding.user.command.application.CreateMemberService;
import com.dnd.gooding.user.command.domain.MemberCreatedEvent;
import org.springframework.stereotype.Component;

@Component
public class MemberCreatedHandler implements EventHandler<MemberCreatedEvent> {

private CreateMemberService createMemberService;

public MemberCreatedHandler(CreateMemberService createMemberService) {
this.createMemberService = createMemberService;
}

@Override
public void handle(MemberCreatedEvent event) {
createMemberService.create(event.getEmail(), event.getoAuthId().getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.dnd.gooding.oauth.command.application.CreateOAuthService;
import com.dnd.gooding.oauth.command.domain.OAuth;
import com.dnd.gooding.token.command.application.TokenService;
import com.dnd.gooding.user.command.application.CreateMemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -17,13 +16,11 @@
public class OAuthLoginController {

@Autowired private CreateOAuthService createOAuthService;
@Autowired private CreateMemberService createMemberService;
@Autowired private TokenService tokenService;

@GetMapping(value = "/login/{code}")
public ResponseEntity<Token> login(@PathVariable String code) {
OAuth oAuth = createOAuthService.createOAuth(code);
createMemberService.createMember(oAuth.getEmail(), oAuth.getoAuthId().getId());
OAuth oAuth = createOAuthService.create(code);
Token token = tokenService.createTokens(oAuth.getEmail());
return ResponseEntity.ok().body(token);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ public CreateMemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}

public void createMember(String id, String oAuthId) {
@Transactional
public void create(String id, String oAuthId) {
MemberId memberId = MemberId.of(id);
Member member =
new Member(memberId, null, null, null, UserRole.ROLE_USER.name(), new OAuthId(oAuthId));
Expand All @@ -30,6 +31,7 @@ public void updateMember(MemberRequest memberRequest) {
memberRepository
.findById(new MemberId(memberRequest.getId()))
.orElseThrow(NoMemberException::new);

member.changeName(memberRequest.getName());
member.changeInterests(memberRequest.getInterests());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.dnd.gooding.user.command.domain;

import com.dnd.gooding.common.event.Event;
import com.dnd.gooding.oauth.command.domain.OAuthId;

public class MemberCreatedEvent extends Event {

private String email;
private OAuthId oAuthId;

private MemberCreatedEvent() {}

public MemberCreatedEvent(String email, OAuthId oAuthId) {
super();
this.email = email;
this.oAuthId = oAuthId;
}

public String getEmail() {
return email;
}

public OAuthId getoAuthId() {
return oAuthId;
}
}
Original file line number Diff line number Diff line change
@@ -1,40 +1,44 @@
package com.dnd.gooding.integration.user.command;

import com.dnd.gooding.integration.IntegrationTest;
import com.dnd.gooding.user.command.application.CreateMemberService;
import com.dnd.gooding.user.query.application.MemberQueryService;
import com.dnd.gooding.user.query.dto.MemberData;
import javax.persistence.EntityManager;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

import com.dnd.gooding.integration.IntegrationTest;
import com.dnd.gooding.user.command.application.CreateMemberService;
import com.dnd.gooding.user.query.application.MemberQueryService;
import com.dnd.gooding.user.query.dto.MemberData;

class CreateMemberServiceIntegrationTest extends IntegrationTest {

@Autowired private CreateMemberService createMemberService;
@Autowired private MemberQueryService memberQueryService;
@Autowired private EntityManager entityManager;

@DisplayName("멤버를 생성한다.")
@Test
void createMember() {
// given
String id = "[email protected]";
String oAuthId = "12356";

// when
createMemberService.createMember(id, oAuthId);
entityManager.flush();
entityManager.clear();

// then
MemberData memberData = memberQueryService.getMember(id);

Assertions.assertEquals(1, 1);
Assertions.assertNotNull(memberData);
Assertions.assertEquals(id, memberData.getId());
Assertions.assertEquals(oAuthId, memberData.getoAuthId());
}
@Autowired
private CreateMemberService createMemberService;
@Autowired
private MemberQueryService memberQueryService;
@Autowired
private EntityManager entityManager;

@DisplayName("멤버를 생성한다.")
@Test
void create() {
// given
String id = "[email protected]";
String oAuthId = "12356";

// when
createMemberService.create(id, oAuthId);
entityManager.flush();
entityManager.clear();

// then
MemberData memberData = memberQueryService.getMember(id);

Assertions.assertEquals(1, 1);
Assertions.assertNotNull(memberData);
Assertions.assertEquals(id, memberData.getId());
Assertions.assertEquals(oAuthId, memberData.getoAuthId());
}
}
Loading

0 comments on commit bd2aedd

Please sign in to comment.