디자인패턴
싱글톤패턴
하나의 인스턴스만 사용되는 경우. 하나의 인스턴스가 사용되지 않는 경우에는 인스턴스의 멤버변수에 저장/입력된 값들이 다른 인스턴스에 공유되지 않는다. 이로인해서 예상한 데이터를 찾을 수 없어서 문제가 발생하기도 함.
빌더패턴
- 장점1 : Setter대신 빌더패턴을 사용하면, 한번 생성된 instance의 값은 변경되지 못하게 하여 thread safe를 확보할 수 있음
- 장점2 : 순차적으로 field를 하나씩 설정할 수 있어서 readbility가 증진됨. 필요한 데이터만 설정할 수 있어서 유연성이 증가됨
- 장점3 : 필요한 데이터만 설정할 수 있어서 유연성이 증가됨
스태틱팩토리패턴
프록시패턴
프록시 패턴의 핵심은 바로 인터페이스의 동일성을 유지함으로써, 클라이언트가 실제로 대리 객체를 사용하고 있는지, 아니면 실제 객체를 사용하고 있는지 신경 쓸 필요가 없도록 만드는 데 있습니다. 서버는 대리객체를 통해서 원래 객체를 호출하고 추가적인 로직을 실행한다.
프록시의 장점은 원래 실제 객체에 기능을 추가하거나 변경하지 않고도, 그 객체에 대한 접근 방식이나 동작을 확장할 수 있게 만드는 것입니다.
- 접근 제어: 프록시는 원래 객체에 대한 접근을 제어할 수 있습니다. 예를 들어, 특정 조건에서만 실제 객체에 접근하도록 하거나, 접근 전에 인증 및 권한 검사를 수행할 수 있습니다.
- 기능 확장: 프록시는 원래 객체의 메서드를 호출하면서 추가적인 기능(예: 로깅, 트랜잭션 관리, 성능 모니터링 등)을 수행할 수 있습니다.
- 지연 초기화(Lazy Initialization): 프록시를 사용하면 실제 객체를 필요한 시점까지 생성하지 않고 미뤄둘 수 있습니다.
퍼사드 패턴 (Facade Pattern)
퍼사드 패턴은 시스템의 복잡성을 숨기고 단순한 인터페이스를 제공하여 클라이언트가 시스템과 상호작용하기 쉽게 만드는 데 사용됩니다. 여러 개의 복잡한 서브시스템이나 서비스가 존재할 때, 퍼사드는 그 모든 기능을 감싸서 하나의 간단한 인터페이스를 제공합니다.
- 예를 들어, 어떤 소프트웨어 시스템이 여러 개의 모듈(결제 모듈, 사용자 관리 모듈, 알림 모듈 등)을 포함하고 있다면, 퍼사드는 이러한 모듈들의 복잡한 API를 숨기고, 하나의 단순한 메서드 호출로 모든 작업을 수행할 수 있게 만들어줍니다.
- 이를 통해 클라이언트는 내부 시스템의 복잡한 구성 요소를 알 필요 없이 단순히 퍼사드를 호출하여 작업을 완료할 수 있습니다.
퍼사드 패턴의 장점은 사용의 용이성과 내부 구현의 캡슐화입니다. 클라이언트는 시스템의 복잡한 내부 구현을 알 필요 없이 간단한 인터페이스만 사용하여 기능을 이용할 수 있습니다.
디자인원칙
Clean Architecture Principles
클린 아키텍처는 소프트웨어 시스템을 여러 계층으로 구분하고, 각 계층 간의 의존성을 엄격하게 관리하는 것을 기본 원리로 합니다. 이를 위해서 의존성역전규칙, 계층간 책임분리을를 준수합니다.
DIP : Dependency Inversion Principle 의존성 역전 원칙(교체)
- 하위모듈의 변경이 상위모듈의 변경을 요구하는 의존성을 끊어내야한다.
https://en.wikipedia.org/wiki/Dependency_inversion_principle
1. High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces).
- 고수준 모듈(내부 계층, 비즈니스 로직)이 저수준 모듈(외부 계층, 구체적 구현)에 직접 의존해서는 안 됩니다.
대신, **추상화 (인터페이스)**를 통해 둘을 연결해야 합니다.
2.Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.
- 추상화는 구체적인 세부 사항에 의존해서는 안 된다. 오히려 세부 사항(구체적 구현)이 추상화에 의존해야 한다.
-> 의존성규칙(Dependency rule) : Inward Dependency
의존성은 항상 안쪽으로(내부로), 즉 더 높은 수준의 추상화 계층(Entities와 Use Cases)으로 향해야 한다는 것입니다.
- 컨트롤러나 필터는 어댑터를 호출하여 데이터를 추출하고, 내부 로직에서 사용할 수 있는 형태로 전달할 수 있습니다.반면, 어댑터는 컨트롤러나 필터를 의존해서는 안 됩니다.
계층간 책임 분리
- Entities
- 엔티티는 소프트웨어에서 문제 해결을 위해 핵심이 되는 업무 규칙들이 속하는 계층으로, 도메인이라고 할 수 있다.
- 엔티티는 단순히 속성들로만 구성된 객체일수도, 속성과 메소드로 구성된 객체일 수 있다.
- Usecase
- 유즈케이스는 어플리케이션 수준의 업무 규칙들이 속하는 계층이다. 즉, 비즈니스로직이 존재하는 곳이다.
- 비즈니스 로직은 소프트웨어 애플리케이션에서 데이터가 처리, 저장, 변경되는 방식을 안내하는 일련의 알고리즘과 규칙입니다.
- 유즈케이스는 어플리케이션 상에서 엔티티 간 데이터 통신을 관장하고, 또한 엔티티의 업무 규칙들이 어플리케이이션 상황에서 실행되도록 하는 코드들이 포함된다.
- 유즈케이스는 어플리케이션 수준의 업무 규칙들이 속하는 계층이다. 즉, 비즈니스로직이 존재하는 곳이다.
- Interface adapter
- 인터페이스 어댑터는 외부에서 유입되는 데이터를 유즈케이스와 엔티티에서 사용하기 편한 방식으로, 또 반대 방식으로 데이터를 변환하는 것을 담당하는 계층이다.
- 계층형 아키텍처에서 presentation에 속하는 controller와 같은 것이 이 계층에 속할 수 있다.
- Frameworks and drivers
- 소프트웨어의 기술적 세부 사항(웹 프레임워크, 데이터베이스, 외부 시스템 등)으로, 외부 입력(예: HTTP 요청, 데이터베이스 드라이버, 메시지 등)을 내부 계층으로 전달하거나 외부 시스템과 통신하는 역할을 담당하는 구성 요소입니다.
https://www.spaceteams.de/en/insights/clean-architecture-a-deep-dive-into-structured-software-design
CleanArchitecture : Division of Layer
Client:
- 프론트엔드 모듈을 의미하며, 사용자 인터페이스를 통해 서버와 상호작용합니다.
- 사용자가 입력한 데이터를 서버로 보내고, 서버로부터 받은 데이터를 화면에 표시합니다.
DTO (Data Transfer Object):
- 계층 간 데이터 교환을 위한 객체입니다.
- 주로 클라이언트에서 서버까지 데이터를 전달할 때 사용됩니다.
- DTO는 범용으로 쓰지 않고 개별적인 용도로 써야 오류가 나지 않는다. 메소드의 필요에 따라 1:1로 만들것. (Controller DTO는 Controller에서만, Service DTO는 Service에서만)
- Service DTO는 Entity에서 일부 데이터를 추출하여 Web Layer에서 필요로 할만한 데이터 전송에 최적화된 형태로 설계된다.
- 반면 Request DTO는 Web의 요구에 따라서 변경이 잦으므로, Service Layer에 침투되지 않도록 분리한다. 필요시에 Service DTO로 변환하여 전달한다.
Filter
- 인증, 권한 확인, 로깅, CORS 처리, 요청/응답 데이터 변환 등을 처리합니다.요청이나 응답을 수정하거나, 특정 조건에서 요청 처리를 중단할 수도 있습니다.
구체적인 책임:
- 인증(Authentication): 요청 헤더에 포함된 인증 토큰(JWT 등)을 검증하고 인증된 사용자인지 확인합니다.
- 권한 부여(Authorization): 사용자의 권한을 검사하여 특정 리소스에 접근할 수 있는지 확인합니다.
- 로깅(Logging): 요청/응답 데이터를 기록하여 디버깅 및 추적에 도움을 줍니다.
- CORS 처리: 다른 도메인에서의 요청을 허용하거나 차단합니다.
- 요청/응답 변환: 요청 본문을 수정하거나 추가 헤더를 삽입하고, 응답 데이터를 변환합니다.
예외처리:
- 필터 단계에서 발생한 예외는 주로 Spring Security의 예외 번역기(ExceptionTranslationFilter) 또는 전역 예외 처리기를 통해 처리됩니다.
Controller:
- 클라이언트로부터 HTTP 요청을 받아들이는 역할을 합니다.
- 요청을 적절한 서비스 계층으로 전달하고, 서비스로부터 받은 결과를 클라이언트에 반환합니다.
- 주로 @Controller나 @RestController 애노테이션을 사용하여 정의합니다.
- 예외처리 : 우선, Contoller별로 custon excpetion Handler로 1차 처리 후, Global Handler을 통해서 전체 예외처리
Service:
- 비즈니스 로직을 처리하는 계층입니다.
- 그렇다면 비즈니스 로직이란 ?
- 상세 구현 로직은 잘 모르더라도 비즈니스의 흐름은 이해 가능한 로직이어야 한다. 신규 개발자 외에도 사업 담당자나 영업 담당자에게까지 코드를 보면서 `이런 흐름이다`라고 설명이 가능한 수준이면 가장 이상적일 것 같습니다.반대로말하면, 비즈니스로직에 상세구현로직은 들어갈필요가 없다. 그리고 비즈니스의 흐름에 대한 것은 모두 비즈니스 로직에 담겨야한다.
- Business logic is a set of algorithms and rules that guide how data is processed, stored and changed in a software application
- Controller로부터 받은 DTO를 사용하여 필요한 로직을 수행하고, 이를 사용하여 Entity를 생성하거나 업데이트합니다. 로직이 다 수행되었다면 Controller에 응답을 위해서 return값을 설정하고 주로 동일한 DTO를 쓴다.
- 주로 @Service 애노테이션을 사용하여 정의합니다.
- 예외처리 : 비즈니스 로직은 예외처리까지 포함하는 범위이며 로직의 원자성을 위해서 excpetion은 Service 내에서만 발생하도록 하는 것이 best practice.
비즈니스레이어에는 어떤 것이 들어가야하는가 ?
지속 성장 가능한 소프트웨어를 만들어가는 방법
스프링은 국내에서 정말 많이 쓰이고 있습니다, 개인적으로 많은 회사를 다녀보며 주니어/시니어를 막론하고 많은 분들이 스프링에 함몰되어 개발을 하고 있다는 느낌을 받을 때가 많았고 이
geminikims.medium.com
Entity (Domain):
- DB의 테이블과 1:1로 대응되는 객체입니다.
- 테이블의 컬럼과 엔티티의 필드가 매핑되며, 여러 엔티티 간의 연관관계(관계형 데이터베이스의 외래키 등)를 정의합니다.
- 주로 @Entity 애노테이션을 사용하여 정의합니다.
Repository:
- DB와 직접 연결되어 CRUD(Create, Read, Update, Delete) 작업을 수행하는 계층입니다.
- Entity에 접근하기 위한 메서드들을 정의하며, Spring Data JPA의 경우 JpaRepository 인터페이스를 상속받아 구현합니다.
- 주로 @Repository 애노테이션을 사용하여 정의합니다.
DAO (Data Access Object):
- 데이터베이스에 접근하는 객체로, 퍼시스턴스 계층이라고도 불립니다.
- 서비스 계층이 DB와 통신할 수 있도록 도와줍니다.
- Spring Data JPA를 사용할 경우, Repository가 DAO의 역할을 합니다.
ServiceImpl, DAOImpl:
- 서비스와 DAO는 인터페이스로 정의되며, 실제 구현 클래스는 Impl 접미사를 붙여서 구현합니다.
- 인터페이스를 통해 의존성을 주입받아 테스트 용이성과 코드의 유연성을 높입니다.
Scheduler :
- Fetching the list of participations eligible for deposit return is triggered periodically, making it a natural responsibility for a scheduler.
- The scheduler is not performing the core business logic; it is merely triggering and coordinating the execution of this logic.
Design Concern
- Seperation between the database architecture(Db Interaction,Entity) from the API(User Interaction, DTO)
- 1. Performance Concerns :
- lazy-loaded
- 2. Transactional Concerns :
- where lazy loading triggers outside of transactional boundaries, causing exceptions like LazyInitializationException.
- 3. Data Safety :
- If entities are exposed directly, any modifications made to the returned entities could inadvertently affect the database once the transaction commits.
- + sensitive info in Entity
- 4. Division of work
- API와 DB에서 요구하는 데이터가 상이할 수 있다. API의 요구에 따라서 ENtity를 수정하는 것은, DB에 영향을 주기에 불가능하고 API요구에 맞도록 DTO를 별도로 만들어주는 것이 올바른 접근이다. 혹시 API의 요구사항 변경이 생겼을때 더욱 유연하게 대응할 수 있음.
- API Stability vs. Database Changes: . Using DTOs allows the database model to evolve without necessarily impacting the API.
- Entity에서 DTO로 넘겨보내기 위해서 DTO 내부구조를 아래와 같이 entity에서 정보를 뽑아서 DTO를 생성하도록 주로함.
- 1. Performance Concerns :
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class AccountDto {
private Long userId;
private String accountNumber;
private Long balance;
private LocalDateTime registeredAt;
private LocalDateTime unregisteredAt;
public static AccountDto fromEntity(Account account) {
return AccountDto.builder().
userId(account.getAccountUser().getId())
.accountNumber(account.getAccountNumber())
.balance(account.getBalance())
.registeredAt(account.getRegisteredAt())
.unregisteredAt(account.getUnregisteredAt()).
build();
'개발기술 > 설계|디자인패턴' 카테고리의 다른 글
비동기 프로그래밍 패턴과 처리 방식 (1) | 2024.11.10 |
---|---|
프로젝트 설계 및 문서화 (0) | 2024.08.14 |
성능 최적화 전략 (0) | 2024.08.02 |
Java programming OOP 연습케이스(1) (0) | 2024.05.13 |
Spring 도입배경 - 객체지향 디자인원리 (1) | 2024.05.06 |