서비스 계층(Service layer), 다른 이름으로는 비즈니스 로직 계층은 아키텍처의 가장 핵심적인 비즈니스 로직을 수행하고 클라이언트가 원하는 요구사항을 구현하는 계층이다.
프레젠테이션 계층
과 데이터 엑세스 계층
사이에서 중간 다리 역할
을 하며, 서로 다른 두 계층이 직접 통신하지 않게 만들어 준다.
- 사용자의 유즈 케이스(Use Case)와 워크플로우(Workflow)를 명확히 정의하고 이해할 수 있도록 도와줍니다.
- 비즈니스 로직이 API 뒤에 숨겨져 있으므로, 서비스 계층의 코드를 자유롭게 수정하거나 리팩터링할 수 있습니다.
- 저장소 패턴(Repository Pattern) 및 가짜 저장소(Fake Repository)와 조합하면 높은 수준의 테스트를 작성할 수 있습니다.
- 서비스 계층 또한 다른 추상화 계층이므로, 잘못 사용하면 코드의 복잡성을 증가시킬 수 있습니다.
- 한 서비스 계층이 다른 서비스 계층에 의존하는 경우, 의존성 관리가 복잡해질 수 있습니다.
- 서비스 계층에 너무 많은 기능을 넣으면 빈약한 도메인 모델(Anemic Domain Model)과 같은 안티 패턴이 생길 수 있습니다.
3계층 아키텍처 - 서비스
auth-service.js
// auth-service.js
import { UsersRepository } from "../repositories/users.repository.js";
import bcrypt from "bcrypt";
export class UsersService {
usersRepository = new UsersRepository();
// 회원 가입
signup = async (email, password, nickname) => {
const hashedPassword = await bcrypt.hash(password, 12);
// 저장소에게 데이터를 요청한다.
const createdUser = await this.usersRepository.signup(
email,
hashedPassword,
nickname
);
// 비즈니스 로직을 수행한 후 사용자에게 보여줄 데이터를 가공한다.
return {
email: createdUser.email,
password: createdUser.password,
nickname: createdUser.nickname,
};
};
// 회원 정보 조회
userInfo = async (userid, userId) => {
const user = await this.usersRepository.userInfo(userid, userId);
return {
userId: user.userId,
email: user.email,
nickname: user.nickname,
createdAt: user.createdAt,
updatedAt: user.updatedAt,
};
};
}
서비스 계층에서 AuthService
클래스가 AuthRepository
의 signup
, userInfo
메서드를 호출하는 것을 볼 수 있다, 해당 코드는 서비스가 비즈니스 로직을 수행하는 데 필요한 데이터를 저장소 계층에게 요청하여 가져오는 것을 확인할 수 있다.
또한, 서비스 계층에서는 데이터를 가공하는 작업이 이루어진다, 만약 저장소 계층에서 받은 데이터를 그대로 클라이언트에게 전달 한다면, 사용자의 비밀번호와 같은 민감한 정보까지 노출되는 보안 문제가 발생하여, 서버의 보안성이 떨어지는 결과를 낳게 된다.