어떤 목적을 수행하기 위해 분리된 작은 구성 단위
정리
모듈은 연관된 코드의 묶음이 모듈성을 갖춘 경우 모듈이라고 부를 수 있다.
모듈성에는 독립성과 은닉성이라는 특징이 있다.
모듈성을 갖출 수 있게 도와주는 시스템이 모듈 시스템이다.
모듈 시스템은 모듈성 중 독립성을 지원하기 위해 의존성 관리 기능을 지원해야 한다.
모듈 시스템은 모듈성 중 은닉성을 지원하기 위해 캡슐화 관리 기능을 지원해야 한다.
모듈 시스템을 통해 이 모듈의 이름은 무엇이고, 어떤 클래스가 선언돼 있으며, 어떤 외부 모듈에 의존하는지, 내부 컴포넌트 중 어떤 컴포넌트를 외부로 공개할지 정의한다.
(Manager?)
여기서 모듈 시스템에 사용된 import와 export는 어떤 의미인가?
import : 이 모듈은 어떤 모듈에 의존하고 있는지 나타낸다.
(해당 모듈을 사용하기 위해 어떤 부수적인 모듈이나 컴포넌트가 필요한지를 선언하는 역할을 한다.)
exports : 이 모듈 중 어떤 컴포넌트만 외부로 노출할 것인지 나타낸다.
(모듈의 캡슐화를 위해 사용되며, 해당 모듈을 임포트하면 어떤 컴포넌트를 사용할 수 있는지 나열하는 역학을 하고,
그 덕에 외부로 공개할 컴포넌트를 제한할 수 있다.)
import * as log4js from "log4js";
const logger = log4js.getLogger();
function printFoobar(){
logger.info("foobar");
}
export fuction printHelloWorld(){
logger.info("Hello world!");
}
lo4js를 import해서 printFoobar()와 printHelloWorld()를 생성했지만,
사용자는 오로지 printHelloWorld()만 사용가능하다.
자바의 패키지는 연관성 높은 코드를 묶는 수단에 불과하므로, 폴더 개념에 더 가깝다.
module myproject.main{
requires org.apache.httpcomponents.httpclient;
exports com.myorg.myproject.model;
}
requires를 이용해 외부 의존성을 관리하고,
exports로 외부로 노출할 모듈 내 인터페이스 패키지를 관리한다.
( 패키지 시스템은 모듈 시스템이 될 수 없다. )
정리
모듈은 모듈성을 만족한다.
모듈성은 독립성과 은닉성을 가지고 있다.
모듈 시스템은 코드 묶음이 모듈성을 갖출 수 있게 도와준다.
모듈 시스템은 기능적으로 모듈 수준의 의존성 관리와 캡슐화 관리가 가능해야 한다.
패키지와 모듈은 다르다.
패키지 시스템은 모듈 시스템이 아니다
패키지를 만들 때도 모듈처럼 독립성과 은닉성을 추구해야 한다.
모듈 시스템은 모듈을 만들기 위한 수단 중 하나인데, 모듈은 모듈 시스템 없이도 만들 수 있다.
모듈이 다른 모듈이나 컴포넌트에 강하게 의존하지 않고 각 모듈을 개별적으로 수정하거나 교체가 가능해야 한다.
(유지보수, 확장성, 재사용성)
모듈을 사용하기 전에 필요한 의존성을 알 수 있어야 한다.
모듈의 의존성이 모두 준비된다면 모듈을 사용하는 데 아무런 문제가 없어야 한다.
Ex)
package com.myproject.account;
public class Account {}
// account보다 상대적으로 독립성이 떨어진다.
package com.myproject.post;
import com.myproject.account.Account;
public class Post{
Account writer;
}
독립적이다라는 뜻은 다음과 같다.
최대한 내부에서 해결하라
외부에 강하게 의존하지 마라
외부 시스템을 사용한다면 외부 시스템의 사용을 명시하라(핵심!)
외부 시스템의 사용을 명시하는 것은
애플리케이션을 실행하는 데 필요한 하위 의존성을 모두 가져오는 것과 동일하다.(의존성 명시와 동일)
(그렇다 해도, 의존성 관리가 핵심이다.)
하위 의존성 추적 기능의 장점
모듈 불러오기 자동화
하위 모듈의 중복 및 불필요한 의존성 검사
하위 모듈 에러시 상위 원인 모듈 파악 가능
동적 로딩 가능
모듈은 은닉성을 추구해야 한다.(캡슐화 가능)
그러나, 모듈을 외부로 공표한다는 것은 모든 사용자를 위한 하위 호환성을 보장해야 함을 의미하므로,
결론적으로 모듈 수준의 인터페이스 관리가 필요함을 뜻한다.
(모듈의 사용자는 모듈이 책임지는 공개된 일부 기능에만 접근할 수 있어야 한다.)
Ex. Micro Service Architecture
MSA는 거대한 시스템의 독립된 일부 도메인이다.
MSA는 외부 서비스와 협력한다.
계층 기반 구조
com.company.project.layer(presentation, service, ...).*
도메인 기반 구조
com.company.project.domain.*
레이어드 아키텍처를 사용하는 프로젝트에 주로 사용.
com.demo.myapp
.presentation
.UserController.java
.CafeController.java
.BoardController.java
.business
.UserService.java
.CafeService.java
.BoardService.java
.repository (interface)
.UserRepository.java
.CafeRepository.java
.BoardRepository.java
.domain
.User.java
.Cafe.java
.Board.java
.infrastructure
.UserRepositoryImpl.java (repository 패키지 내 인터페이스 구현체)
.UserJpaEntity.java
.UserJpaRepository.java
.CafeRepositoryImpl.java
.CafeJpaEntity.java
.CafeJpaRepository.java
.BoardRepositoryImpl.java
.BoardJpaEntity.java
.BoardJpaRepository.java
장점
이해하기 쉽고, 사용하기 쉽다
단점
도메인이 눈에 들어오지 않는다.
애플리케이션에 어떤 도메인이 사용되는지 파악하려면 모든 계층을 열어보고 정리해야만 한다.
(도메인의 관점의 응집도가 떨어지고, 그 결과 비즈니스 코드를 한곳에 모아 볼 수 없다.)
Ex.
UserService는 User 기반은로 작성되고, UserController는 UserService 기반으로 작성되니
하나로 묶는 것이 낫다.
패키지는 논리적으로 연관된 클래스 파일들을 모아 놓는 공간임을 명심해야 한다.
DDD를 이용하는 프로젝트에 자주 사용
.com.demo.myapp
.user
.UserController.java
.UserService.java
.UserRepository.java
.User.java
...
.cafe
.CafeController.java
.CafeService.java
.CafeRepository.java
.Cafe.java
...
패키지 최상단에 프로그램이 사용하는 도메인이 오도록 구성한다.
장점
비즈니스 코드가 여기저기 산재되지 않는다.
최상단 도메인을 보고 어떤 도메인을 사용하는 프로젝트인지 알 수 있다.
단점
계층이 눈에 들어오지 않는다.
.com.demo.myapp
.user
.presentation
.UserController.java
.application
.UserService.java
.repository
.UserRepository.java
.domain
.User.java
.Infrastructure
.UserRepositoryImpl.java
.UserJpaEntity.java
.User JpaRepository.java
.cafe 이하동문.
도메인 기반 구조는 계층 기반 구조보다는 복잡하다.
도메인 기반 구조는 도메인이 눈에 들어온다.
프로젝트를 구성하는 주요 단위를 도메인으로 보고 있다.
그냥 언제 어디서든 독립성과 은닉성을 습관적으로 추구하라.
(패키지든 ,클래스든, API든 다 따져라.)