GithubHelp home page GithubHelp logo

meloning / mega-coffee-employee-manage-project Goto Github PK

View Code? Open in Web Editor NEW
10.0 1.0 1.0 563 KB

클린 아키텍처, 멀티모듈기반 사이드 프로젝트입니다.

Kotlin 92.96% HTML 7.04%
clean-architechture multi-module server side-project

mega-coffee-employee-manage-project's Introduction

메가커피 직원 관리 프로젝트

이 프로젝트는 메가커피 매장들의 직원들 교육을 관리하는 프로젝트입니다.

멀티모듈 기술 기반으로 사이드 프로젝트 목적이 큰 프로젝트입니다.

핵심 기능은 매장 설립시 등록된 매장 내 직원들에게 교육 프로그램에 대한 알림 서비스를 제공하는 것이 핵심입니다.

요구사항

아래 요구사항 내용은 사이드 프로젝트 진행을 위해 가상으로 기획하여 정리한 내용입니다.

구조

버전별 시스템 아키텍처와 프로젝트 모듈 구조를 wiki를 통해 정리하였습니다.

<시스템 아키텍처>

<프로젝트 구조>

테스트

도메인별 유닛 테스트와 RestAssured + TestContainer를 조합한 API 통합 테스트를 중심으로 테스트 코드가 작성되어 있습니다.

TestContainer는 MySQL, RabbitMQ 모듈 각각에서 관리되도록 하여 응집력있는 테스트 설정을 구성하였고, TestFixture를 통해 의존하는 타 모듈의 테스트 영역에 TestContainer를 적용할 수 있도록 구성하였습니다.

(조만간 Wiki 문서를 통해 정리하여 내용 공유할 예정입니다.)

API 문서

REST Docs + epages + Redoc 조합으로 OAS를 만들어 API 문서를 생성하였습니다.

https://meloning.github.io/redoc-meloning-api/

Reference

사이드 프로젝트 진행과정에서 학습 및 참고했던 레퍼런스입니다.

mega-coffee-employee-manage-project's People

Contributors

meloning avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

furansujin

mega-coffee-employee-manage-project's Issues

연관관계 개선

  • 교육프로그램은 여러 교육장소들을 가질 수 있으며, 하나의 교육장소는 반드시 하나의 교육프로그램에 속해야한다.

    • 교육프로그램이란 도메인에 교육 장소는 교육프로그램의 개인소유가 될 수 있다. (AggregateRoot => 교육프로그램)
    • 교육장소들의 관리 주체는 교육 프로그램이 되어 영속성 전이를 통해 교육장소들을 관리할 수 있다. (AggregateRoot의 라이프사이클을 갖는다)
  • 매장은 여러 교육프로그램을 들을 수 있고, 하나의 교육 프로그램은 여러 매장이 들어야할 교육 프로그램이 될수 있다.

    • 매장_교육프로그램 Relation 엔티티는 매장과 교육프로그램 도메인에 개인소유가 될 수 있는가? (X)
    • 매장_교육프로그램 Relation 엔티티는 독립된 Aggregate로 취급해야하며 어느 AggregateRoot의 동일한 라이프사이클을 가질 수 없기에 영속성 전이를 통한 관리도 이루어 질 수 없다. (Relation을 위한 Repostiory가 별도로 필요하다)
  • 유저는 여러 교육장소를 선택하여 등록할 수 있으며, 하나의 교육장소는 여러 참여자(유저)들을 수용할 수 있다.

    • 유저_교육장소 Relation 엔티티는 유저와 교육장소 도메인에 개인소유가 될 수 없다.
    • 비즈니스 로직상 유저가 교육장소 등록을 신청하는 관점으로 넓혀지게 되면 내부적으로 승인절차 개념이 나올수도 있기 때문에 어느 도메인의 개인소유가 될 수 없고 영속성 전이또한 가질 수 없다.
    • 독립된 Relation 엔티티로 취급해야하며 별도 Repository로 만들어 관리해야한다.

RestAssured 로 API 통합 테스트 코드 작성

  1. TestContainer를 도입하자. (core, common 모듈에는 적용되지 않게 구성할 것)
    • Q. MySQL과 같은 RDB에도 적용해야 할까?
      • 만약 DB에 의존적인 함수를 쓰는 등 DB 벤더마다 특화된 기능을 활용하고 있는 경우라면 TestContainer를 통해 일관된 통합테스트를 보장해한다고 생각합니다.

Object Mother 패턴으로 테스트 객체 구조 개선

DomainFixture 클래스를 통해 어느정도 변하지 않은 테스트 객체 생성을 한곳에서 관리하고자 만들어 사용했습니다.

하지만 매번 상황에 따른 필요한 데이터를 만드는 시점에 별도로 넣어 만들고 사용할려다 보니 유지보수하기 어려운 부분이 있었습니다.

Type 또는 상황별 만들고자 하는 테스트 객체를 고정값으로 factory method를 통해 만들도록 처리하고, validation 조건에서 벗어나지 않은 범위 내에서 랜덤하게 값을 생성하여 테스트객체가 generate 되도록 하는 것이 필요했습니다.

이를 Object Mother 패턴 구조와 EasyRandom 을 활용하여 테스트 객체 생성 구조를 개선하고자 합니다.

유저의 교육장소 등록 API 동시성 발생에 따른 문제 해결

유저(직원)가 교육 장소를 등록할때 동시에 같은 장소를 등록할 경우 수강인원 검증 처리에 대한 작업이 필요합니다.

현재 신청된 수강인원을 매핑 테이블을 통해 count하여 가져와 비교하는데 역정규화하여 현재 수강인원 필드를 추가함과 동시에 동시성 처리를 위한 Lock을 적용할지등 고민이 필요한 부분입니다. (Redis 이용도 고려할 것)

테스트 코드 개선

  1. 테스트 영역에 MySQL TestContainers를 도입할 예정입니다.
  • H2의 격리수준 레벨 및 지원하는 SQL 함수 차이등으로 프로덕션과 일관된 환경을 갖추기 위해 도입하고자 합니다.
  1. RestAssured를 이용한 API 통합 테스트를 개선하고자 합니다.
  • 시나리오에 맞는 Given - When - Then 로직을 구성할려고 합니다.

교육 장소 네이밍 개선

EducationAddress 보단 EducationPlace가 더 직관적으로 도메인 이름을 알수 있다.

구현된 코드의 일부분에는 Place 이름으로 함수나 변수명을 활용한게 있는데 나중에 바꿔야지하면서 쭉 미뤄온터라 이제야 올바른 이름으로 변경할려고 합니다.

클래스명, 변수명, 함수명, Api Path까지 address를 place로 적절하게 변경할 예정입니다.

Redoc기반 API 문서 배포 파이프라인 구축하기

#2 이슈를 통해 Redoc 문서 템플릿으로 generate될 수 있도록 OAS Spec에 맞게 yaml파일을 성공적으로 만들었습니다.

이제 API 문서를 보여줄 Github Pages용 저장소를 만들고, redoc cli 명령어와 Github Action workflow를 통해 문서 배포 파이프라인을 구축할려고 합니다.

(이거 끝나고 Redoc 도입과정에 대한 내용과 함께 wiki 문서를 전반적으로 깔끔하게 정리한 후, README.md 내용도 그에 맞게 변경할 예정입니다.)

REST Docs + Epages 조합 API Document Generate

프로젝트 root 내 build.gradle.kts

plugins {
    id("org.springframework.boot") apply false
    id("io.spring.dependency-management") apply false

    id("org.jmailen.kotlinter")

    id("com.epages.restdocs-api-spec") apply false  // epages 플러그인 추가 version은 settings.gradle에서 적용할 것.

    kotlin("jvm")
    kotlin("kapt")
    kotlin("plugin.spring") apply false
    kotlin("plugin.jpa") apply false
}

...

val restDocsProject = listOf(
    project(":mega-coffee-api"), // Admin API가 추가될 수 있으니 list로 미리 선언
)
configure(restDocsProject) {
    apply(plugin = "com.epages.restdocs-api-spec")

    extensions.extraProperties["snippetsDir"] = file("build/generated-snippets")

    val epagesVersion: String by project

    dependencies {
        testImplementation("org.springframework.restdocs:spring-restdocs-mockmvc")

        testImplementation("com.epages:restdocs-api-spec:$epagesVersion")
        testImplementation("com.epages:restdocs-api-spec-mockmvc:$epagesVersion")
    }

    /**
     * @see <a href="https://github.com/ePages-de/restdocs-api-spec">Spring REST Docs API specification Integration</a>
     */
    configure<OpenApi3Extension> {
        setServer("http://localhost:9000")
        title = "MGC Employee Manage OpenApi Specification"
        version = "1.0.0"
        format = "yaml"
    }

    tasks.named("bootJar") {
        doLast {
            val sourceDir = project.file("swagger-ui")
            val yamlFile = project.file("${project.buildDir}/api-spec/openapi3.yaml")
            val targetDir = project.file("build/classes/kotlin/main/BOOT-INF/classes/static/swagger-ui")

            copy {
                from(sourceDir) {
                    into(targetDir)
                }
                from(yamlFile) {
                    into(targetDir)
                }
            }
        }
    }
}

이렇게 구성한 후 Redoc으로 열어서 API 문서들을 확인하면 될것 같다.

alt text

비즈니스 로직 내 트랜잭션 범위 정하기

Transaction 과 강결합 된 코드나 TransactionalEventListener 는 자원 활용 측면에서 효율적인지 고민 해보시면 좋을 것 같습니다

UserService 의 경우는 전역적으로 Transaction 가 걸려있는데 
자세히 살펴보진 않았지만 registerEducationAddress 에서 Transaction 이 필요한 부분은 국소적일 것 같습니다

Q. 전역적으로 Transaction을 반드시 걸어야할까?

[Reference]
https://www.youtube.com/watch?v=18C2A56ialY

레퍼런스 영상에서는 트랜잭션 처리를 3가지 방법으로 소개하고 있습니다.

  1. spring-tx 라이브러리를 추가하는 것으로 합의하여 스프링의 @Transactional을 붙이는 것 (spring-context를 추가한 이유와 같음)
  2. jakarta.transaction-api 라이브러리를 추가하여 @Transactional을 활용하는 것
    • spring에 직접적인 침투로 강하게 결합되는것은 아니기 때문에 생산성을 위해 허용하는 쪽도 괜찮다는 의견
  3. transaction 범위를 직접 코드로 잡아 처리하는 것.
    • 비즈니스 로직 내에서 트랜잭션을 적용할 범위를 직접 코드로 명시하여 적용 (TransactionHandler 등)

현재 프로젝트에서는 core 모듈 내 @Transactional 적용을 위해 spring-tx 라이브러리를 추가하여 활용하는 것으로 결정하였습니다.

당연하게도 데이터 무결성을 지키고자 비즈니스 로직 전반에 트랜잭션을 걸어주었는데
위 피드백 의견으로 볼때 과연 트랜잭션 범위가 비즈니스 로직 전체 범위로 잡아야하는지, 전부 무결성 보장을 위한 롤백 대상이 되어야하는지 의구심이 들어 이를 해결해보고자 합니다.

MySQL TestContainer 실행 안되는 이슈

2024-05-06T07:36:29.152638Z 0 [ERROR] [MY-000068] [Server] unknown option '--skip-host-cache'.
2024-05-06T07:36:29.153652Z 0 [ERROR] [MY-013236] [Server] The designated data directory /var/lib/mysql/ is unusable. You can remove all files that the server added to it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.