5. 웹 어댑터 구현하기 의존성 역전 인커밍 어댑터는 애플리케이션 서비스에 의해 구현된 인터페이스인 전용 포트를 통해 애플리케이션 계층과 통신한다. 웹 어댑터(incomming adpter)란? 외부로부터 요청을 받아 애플리케이션 코어를 호출하고 무슨 일을 해야 할지 알려준다. 이때 제어 흐름은 웹 어댑터에 있는 컨트롤러에서 애플리케이션 계층에 있는 서비스로 흐른다 여기서 의존성 역전이란? 애플리케이션 계층은 웹 어댑터가 통신할 수 있는 특정 포트를 제공하고, 서비스는 이 포트를 구현하고, 웹 어댑터는 이 포트를 호출할 수 있다. 그럼 왜 어댑터와 유스케이스 사이에 또 다른 간접 계층을 넣어야 하는가? 애플리케이션 코어가 외부 세계와 통신할 수 있는 곳에 대한 명세가 포트이기 때문이다. 포트를 적절한 곳..
Book
4. 유스케이스 구현하기 도메인 모델 구현하기 한 계좌에서 다른 계좌로 송감하는 유스케이스를 구현한다. Account 엔티티 모델링 입금과 출금을 할 수 있는 계좌를 의미 package com.book.cleanarchitecture.buckpal.account.domain.vo; public class AccountId { private final Long value; public AccountId(Long value) { this.value = value; } public Long getValue() { return value; } } package com.book.cleanarchitecture.buckpal.account.domain; import com.book.cleanarchitecture...
3. 코드 구성하기 계층으로 구성하기 buckapl |--- domain | |----- Account | |----- Activity | |----- AccountRepository | |----- AccountService |--- persistence | |----- AccountRepositoryImpl |--- web | |----- AccountController 문제점 계층으로 코드를 구성하면 기능적인 측면들이 섞이기 쉽다. 애플리케이션의 기능 조각(functional slice)이나 특성(feature)을 구분 짓는 패키지 경계가 업다. 서로 연관되지 않은 기능들끼리 예상하지 못한 부수효과를 일으킬 수 있는 클래스들의 엉망진창 묶음으로 변모할 가능성이 크다. 애플리케이션이 어떤 유스케이스들..
2. 의존성 역전하기 단일 책임 원칙 정의 하나의 컴포넌트는 오로지 한 가지 일만 해야 하고, 그것을 올바르게 수행해야 한다. 컴포넌트를 변경하는 이유는 오직 하나뿐이어야 한다. 만약 컴포넌트를 변경할 이유가 오로지 한 가지라면 컴포넌트는 딱 한 가지 일만 하게 된다. 만약 컴포넌트를 변경할 이유가 한 가지라면 우리가 어떤 다른 이유로 소프트웨어를 변경하더라도 이 컴포넌트에 대해서는 전혀 신경 쓸 필요 없다. 현실은 컴포넌트 간의 의존성을 통해 너무나도 쉽게 변경할 이유라는 것이 전파된다. 어떤 컴포넌트의 의존성 각각은 이 컴포넌트를 변경하는 이유 하나씩에 해당한다. 전이 의존성(transitive dependency, 프로그램이 참조하고 있는 컴포넌트로부터 전의된 의존성)이라고 하더라도 말이다. 컴포넌..
1. 계층형 아키텍처의 문제는 무엇일까? 잘 만들어진 계층형 아키텍처는 선택의 폭을 넓히고 변화하는 요구사항과 외부 요인에 빠르게 적응할 수 있게 해준다. 바로 이게 아키텍처의 전부다. 전통적인 구조 맨 위의 웹 계층(UI)에서는 요청을 받아 도메인 혹은 비즈니스 계층에 있는 서비스로 요청을 보낸다. 서비스에서는 필요한 비즈니스로직을 수행하고, 도메인 엔티티의 현재 상태를 조회하거나 변경하기 위해 영속성 계층(Data Access)의 컴포넌트를 호출한다. 우리가 만드는 애플리케이션의 목적은 무엇인가? 우리는 보통 비즈니스를 관장하는 규칙이나 정책을 반영한 모델을 만들어서 사용자가 이러한 규칙과 정책을 더욱 편리하게 활용할 수 있게 한다. 이때 우리는 상태(state)가 아니라 행동(behavior)을 중..
2장. 의미 있는 이름 의도를 분명히 밝혀라 int d; // 경과 시간(단위 : 날짜) 위의 변수만 봤을때는 경과 시간이라는걸 전혀 알수가 없다. 여기서의 바로 문제는 단순성이 아니라 함축성이다. 코드 맥락이 코드 자체에 명시적으로 드러나 있지 않다는 것이다. public List getThem() { List list1 = new ArrayList(); for (int[] x : theList) if (x[0] == 4) list1.add(x); return list1; 여기서의 문제는? theList에는 뭐가 들어 있는 것인가? theList의 0번째 값이 왜 중요한가? 값 4의 의미는 무엇인가? 함수가 반환하는 리스트 list1을 어떻게 사용하는가? public List getFlaggedCell..
1장. 깨끗한 코드 깨끗한 코드란? 나는 우아하고 효율적인 코드를 좋아한다. 논리가 간단해야 버그가 숨어들지 못한다. 의존성을 최대한 줄여야 유지보수가 쉬워진다. 오류는 명백한 전략에 의거해 철저히 처리한다. 성능을 최적으로 유지해야 사람들이 원칙 없는 최적화로 코드를 망치려는 유혹에 빠지지 않는다. 깨끗한 코드는 한 가지를 제대로 한다. 비야네 스트롭스트룹 / C++ 창시자이자 The C++ Programming Language 저자 철저한 오류 처리도 언급하는 이유는 세세한 사항까지 꼼꼼하게 신경쓰라는 말. 메모리 누수, 경쟁 상태(race condition), 일관성 없는 명명법 등 도 포함 즉, 깨끗한 코드는 세세한 사항까지 꼼꼼하게 처리하는 코드 깨끗한 코드는 단순하고 직접적이다. 깨끗한 코드는..
1장. 데이터베이스와 SQL 1강. 데이터베이스 데이터베이스란? 데이터란 컴퓨터 안에 기록되어 있는 숫자. 데이터베이스란 넓은 의미에서는 데이터의 집합 또는 정리된 데이터. 영구적으로 보존되어야 하므로 휘발성 저장장치가 아닌 비휘발성 저장장치에 저장. 시스템 내의 데이터베이스 데이터센터 내의 서버에서 운영하는게 일반적이지만, POS기기, 휴대폰의 전화번호부도 모두 데이터베이스로 다양한 시스템에서 사용. DB? DBMS? DB란 데이터베이스(저장장치 내에 정리되어 저장된 데이터의 집합)이며, DBMS는 데이터베이스 관리 시스템(DB를 효율적으로 관리하는 S/W) DBMS가 필요한 이유 생산성 검색, 추가, 삭제, 갱신같은 처리 등 기능을 제공 기능성 복수 유저의 요청에 대응하거나 대용량 데이터를 저장하고 ..
아이템 76. 가능한 한 실패 원자적으로 만들라 실패 원자적(failure-atomic)? 호출된 메소드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지해야 한다는 특성 어떻게 하면 실패 원자적 특성을 가진 메서드를 만들수 있을까? 아주 간단한 방법은 불변 객체(아이템 17)로 만드는 것이다. 메서드가 실패하면 새로운 객체가 만들어 지지 않을 수 있으나, 기존 객체가 불안정한 상태에 빠질일은 없다. 왜냐하면 생성 시점에 고정되어 절대 변하지 않기 때문이다. 그럼 가변객체는? 가장 흔한 방법은 작업 수행에 앞서 매개변수를 검사하는 것(아이템 49)이다. 쉽게 말하면 객체 내부 상태가 변하기 전에 잠재적 예외 가능성을 걸러내라는 의미이다. 간단한 Stack 코드를 보자. public Object po..
아이템54. null이 아닌, 빈 컬렉션이나 배열을 반환하라 컬렉션에 값이 없으면? 대부분 null로 처리한다고 한다고 합니다. (전 그런적이 없는데...) return list.isEmtpy() ? null : new ArrayList(list); 이거의 가장 큰 단점은 위의 값을 return하는 메소드를 사용하는 곳에서 null 방지를 해줘야 한다는 점입니다. 이렇게 쓰는 사람들의 의견은 빈 컬렉션을 반환하는 것이 비용이 들기 때문에 null로 처리해야한다는 의견입니다. 그러나 저 의견은 손쉽게 깨트릴수 있습니다. 빈 컬렉션을 만드는 것의 문제점이 아닌 이유 성능 저하의 주범이라고 확인되지 않는 이상 이정도의 성능 차이는 신경 쓸 수준이 못된다. 빈 컬렉션과 배열을 굳이 새로 할당하지 않고 만들수 있..