diff --git a/src/main/java/com/yam/app/account/application/AccountFacade.java b/src/main/java/com/yam/app/account/application/AccountFacade.java index 779cd72..8f0ebe3 100644 --- a/src/main/java/com/yam/app/account/application/AccountFacade.java +++ b/src/main/java/com/yam/app/account/application/AccountFacade.java @@ -4,6 +4,7 @@ import com.yam.app.account.domain.AccountNotFoundException; import com.yam.app.account.domain.AccountReader; import com.yam.app.account.domain.ConfirmRegisterAccountProcessor; import com.yam.app.account.domain.LoginAccountProcessor; +import com.yam.app.account.domain.RegisterAccountConfirmEvent; import com.yam.app.account.domain.RegisterAccountEvent; import com.yam.app.account.domain.RegisterAccountProcessor; import com.yam.app.account.presentation.AccountResponse; @@ -47,6 +48,7 @@ public class AccountFacade { @Transactional public void registerConfirm(ConfirmRegisterAccountCommand command) { confirmRegisterProcessor.registerConfirm(command.getToken(), command.getEmail()); + publisher.publishEvent(new RegisterAccountConfirmEvent(command.getEmail())); } @Transactional(readOnly = true) diff --git a/src/main/java/com/yam/app/account/domain/Account.java b/src/main/java/com/yam/app/account/domain/Account.java index 09c753c..f672bbb 100644 --- a/src/main/java/com/yam/app/account/domain/Account.java +++ b/src/main/java/com/yam/app/account/domain/Account.java @@ -51,4 +51,8 @@ public final class Account { public boolean isValidToken(String token) { return this.emailCheckToken.equals(token); } + + public void addMember(Long memberId) { + this.memberId = memberId; + } } diff --git a/src/main/java/com/yam/app/account/domain/GenerateMemberEvent.java b/src/main/java/com/yam/app/account/domain/GenerateMemberEvent.java new file mode 100644 index 0000000..b114621 --- /dev/null +++ b/src/main/java/com/yam/app/account/domain/GenerateMemberEvent.java @@ -0,0 +1,15 @@ +package com.yam.app.account.domain; + +import lombok.Getter; + +@Getter +public final class GenerateMemberEvent { + + private final Long memberId; + private final String email; + + public GenerateMemberEvent(Long memberId, String email) { + this.memberId = memberId; + this.email = email; + } +} diff --git a/src/main/java/com/yam/app/account/domain/RegisterAccountConfirmEvent.java b/src/main/java/com/yam/app/account/domain/RegisterAccountConfirmEvent.java new file mode 100644 index 0000000..77c1848 --- /dev/null +++ b/src/main/java/com/yam/app/account/domain/RegisterAccountConfirmEvent.java @@ -0,0 +1,13 @@ +package com.yam.app.account.domain; + +import lombok.Getter; + +@Getter +public final class RegisterAccountConfirmEvent { + + private final String email; + + public RegisterAccountConfirmEvent(String email) { + this.email = email; + } +} diff --git a/src/main/java/com/yam/app/account/infrastructure/AccountEventListener.java b/src/main/java/com/yam/app/account/infrastructure/AccountEventListener.java new file mode 100644 index 0000000..c41ca6b --- /dev/null +++ b/src/main/java/com/yam/app/account/infrastructure/AccountEventListener.java @@ -0,0 +1,31 @@ +package com.yam.app.account.infrastructure; + +import com.yam.app.account.domain.AccountNotFoundException; +import com.yam.app.account.domain.AccountReader; +import com.yam.app.account.domain.AccountRepository; +import com.yam.app.account.domain.GenerateMemberEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +public class AccountEventListener { + + private final AccountReader accountReader; + private final AccountRepository accountRepository; + + public AccountEventListener(AccountReader accountReader, + AccountRepository accountRepository) { + this.accountReader = accountReader; + this.accountRepository = accountRepository; + } + + @EventListener + @Transactional + public void handle(GenerateMemberEvent event) { + var account = accountReader.findByEmail(event.getEmail()) + .orElseThrow(() -> new AccountNotFoundException(event.getEmail())); + account.addMember(event.getMemberId()); + accountRepository.update(account); + } +} diff --git a/src/main/java/com/yam/app/adapter/DomainEventTranslator.java b/src/main/java/com/yam/app/adapter/DomainEventTranslator.java new file mode 100644 index 0000000..f2c6256 --- /dev/null +++ b/src/main/java/com/yam/app/adapter/DomainEventTranslator.java @@ -0,0 +1,34 @@ +package com.yam.app.adapter; + +import com.yam.app.account.domain.RegisterAccountConfirmEvent; +import com.yam.app.member.domain.GenerateMemberEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionalEventListener; + +@Component +class DomainEventTranslator { + + private final ApplicationEventPublisher publisher; + + public DomainEventTranslator(ApplicationEventPublisher publisher) { + this.publisher = publisher; + } + + @Async + @TransactionalEventListener + public void handle(RegisterAccountConfirmEvent event) { + publisher.publishEvent( + new com.yam.app.member.domain.RegisterAccountConfirmEvent(event.getEmail())); + } + + @Async + @TransactionalEventListener + public void handle(GenerateMemberEvent event) { + publisher.publishEvent( + new com.yam.app.account.domain.GenerateMemberEvent(event.getMemberId(), + event.getEmail())); + } + +} diff --git a/src/main/java/com/yam/app/member/domain/GenerateMemberEvent.java b/src/main/java/com/yam/app/member/domain/GenerateMemberEvent.java new file mode 100644 index 0000000..37e5a46 --- /dev/null +++ b/src/main/java/com/yam/app/member/domain/GenerateMemberEvent.java @@ -0,0 +1,15 @@ +package com.yam.app.member.domain; + +import lombok.Getter; + +@Getter +public final class GenerateMemberEvent { + + private final Long memberId; + private final String email; + + public GenerateMemberEvent(Long memberId, String email) { + this.memberId = memberId; + this.email = email; + } +} diff --git a/src/main/java/com/yam/app/member/domain/Member.java b/src/main/java/com/yam/app/member/domain/Member.java new file mode 100644 index 0000000..5f0ce4e --- /dev/null +++ b/src/main/java/com/yam/app/member/domain/Member.java @@ -0,0 +1,18 @@ +package com.yam.app.member.domain; + +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public final class Member { + + private Long id; + private String nickname; + private String image; + + public Member(String nickname, String image) { + this.nickname = nickname; + this.image = image; + } +} diff --git a/src/main/java/com/yam/app/member/domain/MemberReader.java b/src/main/java/com/yam/app/member/domain/MemberReader.java new file mode 100644 index 0000000..2b4a534 --- /dev/null +++ b/src/main/java/com/yam/app/member/domain/MemberReader.java @@ -0,0 +1,8 @@ +package com.yam.app.member.domain; + +import java.util.Optional; + +public interface MemberReader { + + Optional findByNickname(String nickname); +} diff --git a/src/main/java/com/yam/app/member/domain/MemberRepository.java b/src/main/java/com/yam/app/member/domain/MemberRepository.java new file mode 100644 index 0000000..a4ef6dd --- /dev/null +++ b/src/main/java/com/yam/app/member/domain/MemberRepository.java @@ -0,0 +1,6 @@ +package com.yam.app.member.domain; + +public interface MemberRepository { + + void save(Member entity); +} diff --git a/src/main/java/com/yam/app/member/domain/RegisterAccountConfirmEvent.java b/src/main/java/com/yam/app/member/domain/RegisterAccountConfirmEvent.java new file mode 100644 index 0000000..72901be --- /dev/null +++ b/src/main/java/com/yam/app/member/domain/RegisterAccountConfirmEvent.java @@ -0,0 +1,13 @@ +package com.yam.app.member.domain; + +import lombok.Getter; + +@Getter +public final class RegisterAccountConfirmEvent { + + private final String email; + + public RegisterAccountConfirmEvent(String email) { + this.email = email; + } +} diff --git a/src/main/java/com/yam/app/member/domain/empty.txt b/src/main/java/com/yam/app/member/domain/empty.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/com/yam/app/member/infrastructure/MemberEventListener.java b/src/main/java/com/yam/app/member/infrastructure/MemberEventListener.java new file mode 100644 index 0000000..9aa5565 --- /dev/null +++ b/src/main/java/com/yam/app/member/infrastructure/MemberEventListener.java @@ -0,0 +1,37 @@ +package com.yam.app.member.infrastructure; + +import com.yam.app.member.domain.GenerateMemberEvent; +import com.yam.app.member.domain.Member; +import com.yam.app.member.domain.MemberReader; +import com.yam.app.member.domain.MemberRepository; +import com.yam.app.member.domain.RegisterAccountConfirmEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +public class MemberEventListener { + + private final MemberReader memberReader; + private final MemberRepository memberRepository; + private final ApplicationEventPublisher publisher; + + public MemberEventListener(MemberReader memberReader, + MemberRepository memberRepository, + ApplicationEventPublisher publisher) { + this.memberReader = memberReader; + this.memberRepository = memberRepository; + this.publisher = publisher; + } + + @EventListener + @Transactional + public void handle(RegisterAccountConfirmEvent event) { + var nickname = event.getEmail().split("@")[0]; + memberRepository.save(new Member(nickname, "temp.png")); + var member = memberReader.findByNickname(nickname) + .orElseThrow(IllegalArgumentException::new); + publisher.publishEvent(new GenerateMemberEvent(member.getId(), event.getEmail())); + } +} diff --git a/src/main/java/com/yam/app/member/infrastructure/MemberModuleConfiguration.java b/src/main/java/com/yam/app/member/infrastructure/MemberModuleConfiguration.java new file mode 100644 index 0000000..5441f14 --- /dev/null +++ b/src/main/java/com/yam/app/member/infrastructure/MemberModuleConfiguration.java @@ -0,0 +1,21 @@ +package com.yam.app.member.infrastructure; + +import com.yam.app.member.domain.MemberReader; +import com.yam.app.member.domain.MemberRepository; +import org.mybatis.spring.SqlSessionTemplate; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MemberModuleConfiguration { + + @Bean + public MemberReader memberReader(SqlSessionTemplate template) { + return new MybatisMemberRepository(template); + } + + @Bean + public MemberRepository memberRepository(SqlSessionTemplate template) { + return new MybatisMemberRepository(template); + } +} diff --git a/src/main/java/com/yam/app/member/infrastructure/MybatisMemberRepository.java b/src/main/java/com/yam/app/member/infrastructure/MybatisMemberRepository.java new file mode 100644 index 0000000..13c212c --- /dev/null +++ b/src/main/java/com/yam/app/member/infrastructure/MybatisMemberRepository.java @@ -0,0 +1,32 @@ +package com.yam.app.member.infrastructure; + +import com.yam.app.member.domain.Member; +import com.yam.app.member.domain.MemberReader; +import com.yam.app.member.domain.MemberRepository; +import java.util.Optional; +import org.mybatis.spring.SqlSessionTemplate; + +public final class MybatisMemberRepository implements MemberRepository, MemberReader { + + private final SqlSessionTemplate template; + + private static final String SAVE_FQCN = "com.yam.app.member.domain.MemberRepository.save"; + + public MybatisMemberRepository(SqlSessionTemplate template) { + this.template = template; + } + + @Override + public Optional findByNickname(String nickname) { + return template.getMapper(MemberReader.class).findByNickname(nickname); + } + + @Override + public void save(Member entity) { + int result = template.insert(SAVE_FQCN, entity); + if (result != 1) { + throw new RuntimeException( + String.format("There was a problem saving the object : %s", entity)); + } + } +} diff --git a/src/main/java/com/yam/app/member/infrastructure/empty.txt b/src/main/java/com/yam/app/member/infrastructure/empty.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/resources/mapper/xml/AccountCommandMapper.xml b/src/main/resources/mapper/xml/AccountCommandMapper.xml index 5df1aac..2a6ead5 100644 --- a/src/main/resources/mapper/xml/AccountCommandMapper.xml +++ b/src/main/resources/mapper/xml/AccountCommandMapper.xml @@ -12,7 +12,8 @@ withdraw = #{withdraw}, joined_at = #{joinedAt}, role = #{role}, - last_modified_at = NOW() + last_modified_at = NOW(), + member_id = #{memberId} WHERE id = #{id} diff --git a/src/main/resources/mapper/xml/MemberCommandMapper.xml b/src/main/resources/mapper/xml/MemberCommandMapper.xml new file mode 100644 index 0000000..caea510 --- /dev/null +++ b/src/main/resources/mapper/xml/MemberCommandMapper.xml @@ -0,0 +1,12 @@ + + + + + + + INSERT + INTO MEMBER(nickname, image) + VALUES (#{nickname}, #{image}) + + + diff --git a/src/main/resources/mapper/xml/MemberQueryMapper.xml b/src/main/resources/mapper/xml/MemberQueryMapper.xml new file mode 100644 index 0000000..fabf30f --- /dev/null +++ b/src/main/resources/mapper/xml/MemberQueryMapper.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 6b04553..3e496e6 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -25,8 +25,5 @@ create table member( alter table account add constraint UK_q0uja26qgu1atulenwup9rxyr unique (email); -alter table member - add constraint UK_hh9kg6jti4n1eoiertn2k6qsc unique (nickname); - alter table account add constraint FKr5j0huynd7nsv1s7e9vb8qvwo foreign key (member_id) references member; diff --git a/src/test/java/com/yam/app/ArchUnitTests.java b/src/test/java/com/yam/app/ArchUnitTests.java index 7080560..a71843b 100644 --- a/src/test/java/com/yam/app/ArchUnitTests.java +++ b/src/test/java/com/yam/app/ArchUnitTests.java @@ -21,10 +21,12 @@ final class ArchUnitTests { .layer("Presentation").definedBy("..presentation..") .layer("Infrastructure").definedBy("..infrastructure..") .layer("Integration").definedBy("..integration..") + .layer("Adapter").definedBy("..adapter..") .whereLayer("Presentation").mayOnlyBeAccessedByLayers("Application", "Integration") .whereLayer("Application").mayOnlyBeAccessedByLayers("Presentation", "Domain") - .whereLayer("Domain").mayOnlyBeAccessedByLayers("Application", "Infrastructure") + .whereLayer("Domain").mayOnlyBeAccessedByLayers("Application", "Infrastructure", "Adapter") .whereLayer("Infrastructure").mayOnlyBeAccessedByLayers("Presentation", "Integration") + .whereLayer("Adapter").mayNotBeAccessedByAnyLayer() .whereLayer("Integration").mayNotBeAccessedByAnyLayer(); }