본문 바로가기

Design Patterns

(12)
마이크로 서비스에서 분산 트랜잭션과 사가 패턴 우리는 애플리케이션을 개발할 때 자연스럽게 트랜잭션을 사용한다. 스프링 프레임워크에서는 선언형 메커니즘(@Transactional)을 제공하여 간편하게 트랜잭셔널 비즈니스 로직을 작성할 수 있다. 그러나 이를 사용하는 데에는 전제가 있다. 해당 비즈니스 로직이 로컬 트랜잭션에서 해결 가능하거나 단일 DB에 접근하는 모놀리식 애플리케이션일 경우이다. 그렇다면 마이크로 서비스로 구성된 경우는 어떨까? Database per service로 구성된 아키텍처에서도 트랜잭션을 적용하는 것이 간단할 수 있을까? 마이크로서비스 환경에서의 트랜잭션 마이크로 서비스 환경에서의 트랜잭션을 설명하기 위해 배달 애플리케이션의 주문을 예로 생각해보자. 주문, 소비자, 주방, 회계 서비스가 있고 주문이 생성된다면 어떤식으로 트랜.. 더보기
프록시, 프록시 패턴, 데코레이터 패턴 프록시 패턴과 데코레이터 패턴 Proxy 란? 클라이언트와 서버 관계는 컴퓨터 세상에서는 흔하게 볼 수 있는 구조다. 네트워크 상의 통신, 하나의 컴퓨터 내부의 프로세스간 통신, 심지어 애플리케이션 내부에서 객체간의 통신에서도 우리는 클라이언트 서버 관계를 쉽게 찾을 수 있다. 여기서는 객체간의 통신에서의 프록시에 대해 알아본다. 클라이언트는 서버에 필요한 것을 요청을 하고 서버는 그에 대한 처리를 하고 응답을 반환한다. 프록시는 이런 클라이언트와 서버 사이에서 요청에 대한 접근 제어, 부가기능 추가 등의 기능을 담당한다. 프록시는 컴퓨터 세상에 다양한 클라이언트 서버 관계들에서 존재할 수 있다. 그리고 클라이언트는 서버로 요청을 보냈지만 이를 처리하는 주체가 프록시인지 실제 서버인지 알 수는 없다. 프.. 더보기
값 객체, 금액, 레코드 집합 값 객체(Value Object) 금액이나 날짜 범위와 같이 동등성의 개념이 식별자에 기반을 두지 않는 작고 간단한 객체 참조 객체와 값 객체를 구분해서 생각하면 유용하다. 예를 들어 값 객체는 금액, 날짜와 같은 작은 객체이고 참조 객체는 주문, 고객과 같이 큰 객체이다. 특징 일반적으로 더 작다. 언어에서 제공되는 기본형(primitive type)과 비슷하다. 작동 원리 읽기 전용으로 만들어야 한다. (Aliasing bug 방지 : 공유 하는 값 객체에서 한 소유자가 변경을 하면 다른 소유자의 객체도 변경된다.) 완성된 레코드로 저장해서는 안 된다. (포함 값 사용) 사용 시점 식별자(identifier)가 아닌 다른 기준을 바탕으로 동등성을 판단하는 경우 값 객체를 사용한다. 생성하기 쉬운 모든.. 더보기
레지스트리 패턴, 특수 사례 패턴 레지스트리 다른 객체가 공용 객체와 서비스를 찾기 위해 사용하는 잘 알려진 객체 어떤 객체를 찾으려는 경우 찾으려는 객체와 연결된 다른 객체에서 시작해 이 연결을 따라 검색하는 것이 일반적이다. 예를들어 한 고객의 모든 주문을 찾으려면 고객 객체에서 주문을 얻는 메서드를 사용한다. 그런데 이렇게 출발점으로 사용할 적절한 객체가 없는 경우가있다. 레지스트리는 기본적으로 전역 객체이거나 보이는 것만큼 전역은 아닐 수 있지만 적어도 전역으로 보인다. 작동 원리 레지스트리의 정적 메서드를 통해서 필드에 접근한다. (필드까지 정적일 필요는 없다) 데이터의 범위 레지스트리의 데이터는 실행 컨텍스트별로 달라질 수 있다. 프로세스 범위의 레지스트리에서 일반적으로 사용되는 형태는 싱글턴이다. 이 경우 레지스트리 클래스는.. 더보기
분리 인터페이스 패턴, 플러그인 패턴 이 포스팅은 엔터프라이즈 애플리케이션 아키텍처 패턴 18장을 요약한 내용입니다. 분리 인터페이스(Seperated Interface) 구현과 분리된 별도의 패키지에 인터페이스를 정의한다. 시스템을 구성하는 부분 간의 의존성이 필요한 경우 인터페이스로 결합도를 최소한으로 가져가면서 의존할 수 있다. 작동원리 특정 객체에서 다른 객체의 기능이 필요할 때 직접 의존하지 않고 인터페이스를 통해서 의존하게 한다. 이 때 구현을 인스턴스화하기가 불편할 수 있는데 구현을 팩터리에 바인딩하는 플러그인 패턴을 사용하면 된다. (굳이 플러그인을 사용하지 않아도 됨) 사용 시점 프레임워크 패키지에 넣은 범용 추상 코드에서 특정한 애플리케이션 코드를 호출해야 한다. 한 계층의 코드에서 볼 수 없어야 하는 다른 계층의 코드를 .. 더보기
매퍼 패턴, 계층 상위 형식패턴 매퍼 독립적인 두 객체 간의 통신을 설정하는 객체 작동원리 두 하위 시스템을 격리한다. 작동 방법은 매퍼가 매핑하는 계층의 종류에 따라 다르다. 사용시점 하위 시스템간의 상호작용에 대한 의존성이 없어야 하는경우 상호작용이 두 시스템의 주요 용도와는 다소 거리가 있을 때 엔터프라이즈 애플리케이션 매퍼는 데이터베이스와의 상호작용을 위한 데이터 매퍼*로 주로 사용된다. (*10장 데이터원본 아키텍처 패턴 참조) 계층 상위 형식(Layer SuperType) 해당 계층에서 모든 형식의 상위 형식 역할을 하는 형식, 공통 부분을 부모 클래스에서 상속받아 사용하자 작동원리 한 계층 내의 모든 객체에 대한 상위 클래스를 만드는 것이다. 사용시점 모든 객체에 적용되는 공통적인 기능이 있을 때 예제: BaseEntity.. 더보기
게이트웨이 패턴, 서비스 스텁 패턴 이 포스팅은 엔터프라이즈 어플리케이션 아키텍처 패턴 18장을 요약했습니다. 게이트 웨이 외부 시스템이나 자원에 대한 접근을 캡슐화하는 객체 aka, 갖다 쓸 때 만들어서 사용하는 API 외부 자원에 접근할 때는 해당 자원의 API를 사용해야 한다. (전용 API) 예를들어, 외부 이메일 시스템을 연동한다고 할때 EmailSender.send(id, emailformat, title, content ...) 와 같이 제공하는 형식을 맞춰서 사용해야 한다. 모든 특수한 API 코드를 일반 객체처럼 보이는 인터페이스를 포함하는 클래스로 래핑하면 이 문제를 간단하게 해결할 수 있다. 작동 원리 외부 자원에 대해 어떤 작업을 수행해야 하는지 고려해서 용도에 맞는 API를 만든 다음 사용하면 끝 주요 용도 서비스 .. 더보기
지연로드(Lazy Load) 패턴 이 포스팅은 어플리케이션 아키텍쳐 패턴 11장을 요약한 내용입니다. 필요한 데이터를 모두 포함하지는 않지만 데이터가 필요할 때 가져오는 방법을 아는 객체 데이터베이스에서 메모리로 데이터를 로드하는 기능을 만들 때 원하는 객체를 로드하면서 연관된 객체를 함께 로드하도록 설계하는 것이 좋다. 이렇게 하면 객체를 사용하는 개발자가 직접 필요한 객체를 모두 로드할 필요 없이 로드 기능을 더 편리하게 사용할 수 있다. 그런데 한 객체를 로드 할 때 연관된 객체가 지나치게 많이 로드되고, 실제로는 소수의 객체만 필요한 상황에서 오히려 성능이 저하되는 경우가 발생할 수 있다. 지연 로드(Lazy Load)는 이 로딩 프로세스를 일시적으로 중단하고 객체 구조에 표시를 남겨서 나중에 데이터가 필요할 때 로드할 수 있게 .. 더보기