๐Ÿก DDD ์ ์šฉํ•˜๊ธฐ - 3

ozzingยท2022๋…„ 11์›” 4์ผ
0

DDD

๋ชฉ๋ก ๋ณด๊ธฐ
3/3

์ด๋ฒˆ ํฌ์ŠคํŠธ๋Š” ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ๊ตฌํ˜„ํ•œ ์œ ์ € ์ •๋ณด ์ˆ˜์ • ์š”์ฒญ์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ์˜ˆ์‹œ๋กœ ์ž‘์„ฑํ•˜์˜€๋‹ค.


1. ํ‘œํ˜„ ์˜์—ญ (Presentation)

์—ญํ• 

ํ‘œํ˜„ ์˜์—ญ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ด์šฉํ•˜๋Š” ํ™”๋ฉด๊ณผ ๊ทธ ํ๋ฆ„์„ ์ œ๊ณตํ•˜๊ณ , ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์„ ์‘์šฉ ์„œ๋น„์Šค์— ์ „๋‹ฌํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋Œ๋ ค ๋ฐ›์•„ ๋‹ค์‹œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ œ๊ณตํ•œ๋‹ค. ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ๋ฅผ ์ž˜ ๋ณ€ํ™˜ํ•˜์—ฌ ์‘์šฉ ์„œ๋น„์Šค์˜ ๋ฉ”์†Œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

ํ•ด๋‹น ์˜์—ญ์—์„œ ์„œ๋น„์Šค์˜ ์ฃผ์š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋…ธ์ถœ๋˜์–ด์„œ๋Š” ์•ˆ๋œ๋‹ค. ๊ณ„์ธต ๊ฐ„ ์˜์กด์„ฑ์ด ๋„ˆ๋ฌด ๋†’์•„์ง€๊ธฐ๋„ ํ•˜๊ณ , ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์ด ํŠธ๋žœ์žญ์…˜ ๋‹จ์œ„๋กœ ์ด๋ฃจ์–ด์ ธ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์˜ˆ์‹œ

์•„๋ž˜ ์˜ˆ์‹œ๋Š” ์œ ์ € ์ •๋ณด ์ˆ˜์ • ์š”์ฒญ์— ๋Œ€ํ•œ ํ‘œํ˜„ ์˜์—ญ ์ฝ”๋“œ์ด๋‹ค.

    @Operation(summary = "์œ ์ € ์ •๋ณด ์ˆ˜์ •")
    @CachePut(value = USER_KEY, key = "#user.getId()", cacheManager = "cacheManager")
    @PatchMapping(value = "", produces = "application/json; charset=utf-8")
    @ResponseBody
    public CommonResponse editUserAccount(@AuthenticationPrincipal User user,
            @Valid @RequestBody UserRequest.EditAccountRequest request) {

        var userCommand = userRequestMapper.of(request);
        var userResult = userFacade.editUserAccount(user, userCommand);
        var response = userResponseMapper.of(userResult);

        return CommonResponse.onSuccess(response);
    }

์ง๊ด€์ ์ธ ๋„ค์ด๋ฐ์œผ๋กœ Facade์—์„œ ๋ฌด์—‡์„ ํ•˜๋Š”์ง€ ํŒŒ์•…ํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ์ •ํ™•ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋„๋ฉ”์ธ ๋กœ์ง์˜ ํ๋ฆ„์ด ๋…ธ์ถœ๋˜์ง€ ์•Š์•˜์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•ด๋‹น ๋ฉ”์†Œ๋“œ์—์„œ๋Š” @AuthentcationPrincipal์„ ํ†ตํ•ด ์ด๋ฏธ JWT ํ† ํฐ ๋ณตํ˜ธํ™”๋กœ ์–ป์€ ์œ ์ € ์ •๋ณด์™€ ์ˆ˜์ •ํ•˜๊ณ ์ž ํ•˜๋Š” ์ •๋ณด request๊ฐ€ RequestBody๋กœ ํ•จ๊ป˜ ์š”์ฒญ๋œ๋‹ค. Mapper ํ™œ์šฉ์„ ํ†ตํ•ด Request์™€ Response๊ฐ€ ๊ฐ ๊ณ„์ธต ๊ฐ„ ๊ทธ๋ฆฌ๊ณ  ๋ฆฌ์Šคํฐ์Šค๋กœ ๋ฐ˜ํ™˜ ๋  ๋•Œ ๊ฐ๊ธฐ ๋‹ค๋ฅธ ๊ฐ์ฒด์— ๋‹ด๊ฒจ ์ „๋‹ฌ๋œ๋‹ค.


2. ์‘์šฉ ์˜์—ญ (Application)

์—ญํ• 

์‘์šฉ ์˜์—ญ์˜ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ๋“ค์–ด์˜จ ์š”์ฒญ์„ Controller๊ฐ€ ํ•ด์„ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์•Œ๋งž๊ฒŒ ๋„˜๊ฒจ์ฃผ๋ฉด ๊ทธ์— ๋”ฐ๋ผ ์ˆ˜ํ–‰ํ•  ์ž‘์—…์„ ์ •์˜ํ•˜๊ณ  ๋„๋ฉ”์ธ ์„œ๋น„์Šค๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

์‘์šฉ ์„œ๋น„์Šค์˜ ์ฃผ๋œ ์—ญํ• ์€ ๋„๋ฉ”์ธ ๊ฐ์ฒด ๊ธฐ๋ฐ˜์˜ ๋กœ์ง์˜ ํ๋ฆ„์„ ์ œ์–ดํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์˜ˆ์‹œ๋กœ ๋„๋ฉ”์ธ ์„œ๋น„์Šค ํ˜ธ์ถœ ์ดํ›„ ๋ณ„๊ฐœ๋กœ ์•Œ๋ฆผ์„ ๋ณด๋‚ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋„ ์•Œ๋ฆผ ์„œ๋น„์Šค๋ฅผ ํ•ด๋‹น ๊ณ„์ธต์—์„œ ํ˜ธ์ถœํ•œ๋‹ค.

์˜ˆ์‹œ

์•„๋ž˜ ์˜ˆ์‹œ๋Š” ์œ ์ € ์ •๋ณด ์ˆ˜์ • ์š”์ฒญ์— ๋Œ€ํ•œ ์‘์šฉ ์˜์—ญ ์ฝ”๋“œ์ด๋‹ค.

    public UserInfo.Main editUserAccount(User user, UserCommand.EditAccountRequest request) {
        return userService.editUserAccount(user, request);
    }

ํ•ด๋‹น ๋ ˆ์ด์–ด๋Š” ์•„์ฃผ ์–‡์€ ๋ ˆ์ด์–ด๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ์„œ๋น„์Šค๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๊ทธ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด์ค€๋‹ค.


3. ๋„๋ฉ”์ธ ์˜์—ญ (Domain)

์—ญํ• 

๋„๋ฉ”์ธ ์˜์—ญ์—์„œ ์ด์ „ ์˜์—ญ๋“ค์—์„œ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š๋Š” ์ค‘์š”ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋“ค์ด ์ด๋ฃจ์–ด์ง„๋‹ค.

Entity๊ฐ€ ์ •์˜๋˜์–ด ์žˆ๋Š” ์˜์—ญ์ด๋ฉฐ, Infrastructure ์˜์—ญ์—์„œ ๊ตฌํ˜„๋˜๋Š” ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์ฝ๊ธฐ, ์“ฐ๊ธฐ ์—ญํ• ์˜ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ •์˜๋˜์–ด ์žˆ๋‹ค. ๋„๋ฉ”์ธ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜จ ๋’ค, ๊ธฐ๋Šฅ์— ๋งž๊ฒŒ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ์‘์šฉ ์˜์—ญ์— ๋„˜๊ฒจ ์ค€๋‹ค.

๋„๋ฉ”์ธ ๋กœ์ง์„ ์‘์šฉ ์„œ๋น„์Šค์—์„œ ํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์ฝ”๋“œ์˜ ์‘์ง‘์„ฑ๋„ ๋‚ฎ์•„์ง€๊ณ  ์ค‘๋ณต ์ฝ”๋“œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด ๊ธฐํ”ผํ•ด์•ผ ํ•œ๋‹ค.

์˜ˆ์‹œ

	private final UserStore userStore;

    @Override
    @Transactional
    public UserInfo.Main editUserAccount(User user, UserCommand.EditAccountRequest request) {
        user.editAccount(request);
        userStore.store(user);
        return userInfoMapper.of(user);
    }

์œ ์ € ๊ฒ€์ฆ์€ JWT ํ† ํฐ์œผ๋กœ ์œ ์ €๋ฅผ ์ธ์ฆํ•˜๋Š” ๊ณผ์ •์—์„œ ์ง„ํ–‰๋˜์–ด ์ƒ๋žตํ•˜์˜€๋‹ค.

Store๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™œ์šฉํ•˜์—ฌ infrastructure ์˜์—ญ์—์„œ ๋ณ€๊ฒฝ๋œ user ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•ด์ค€๋‹ค. ์ดํ›„ Mapper๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ๋ฅผ DTO๋กœ ๊ฐ์‹ธ ์‘์šฉ ์˜์—ญ์œผ๋กœ ์ „๋‹ฌํ•œ๋‹ค.

๋„๋ฉ”์ธ ๊ฐ์ฒด์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋กœ์ง์€ editAccount์—์„œ ํ–‰ํ•ด์ง€๋ฉฐ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

    public void editAccount(UserCommand.EditAccountRequest request) {
        updateName(request.getName());
        updatePlan(request.getPlan());
    }

    public void updateName(String name) {
        if (name != null) {
            this.name = name;
        }
    }

    public void updatePlan(String plan) {
        if (plan != null) {
            this.plan = PlanStatus.valueOf(plan);
        }
    }

4. ์ธํ”„๋ผ์ŠคํŠธ๋Ÿญ์ณ ์˜์—ญ (Infrastructure)

์—ญํ• 

๊ฐ€์žฅ low level ๊ตฌํ˜„์ฒด๋กœ ๊ธฐ์ˆ ์  ๊ธฐ๋Šฅ๋“ค์„ ์ œ๊ณตํ•œ๋‹ค. DIP ๊ฐœ๋…์„ ํ™œ์šฉํ•˜์—ฌ domain ๊ณ„์ธต์—์„œ ์ถ”์ƒํ™” ๋œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

Repository๊ฐ€ ์œ„์น˜ํ•˜๋ฉฐ, ๊ทธ ์™ธ์—๋„ ๋„๋ฉ”์ธ ๊ณ„์ธต์—์„œ ์ •์˜ ๋œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ Repository๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ์˜ˆ์‹œ์—์„œ๋Š” UserStore๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค์— ํ•ด๋‹นํ•˜๋ฉฐ, ์ธํ”„๋ผ์ŠคํŠธ๋Ÿญ์ฒ˜ ์˜์—ญ์—์„œ UserStoreImpl์ด๋ผ๋Š” ๊ตฌํ˜„์ฒด๋ฅผ ์ž‘์„ฑํ•ด์ฃผ์—ˆ๋‹ค.

์˜ˆ์‹œ

    private final UserRepository userRepository;

    @Override
    public User store(User user) {
        return userRepository.save(user);
    }

ํŠธ๋žœ์žญ์…˜

DB์˜ ACID ์„ฑ์งˆ ๋ณด์žฅ์„ ์œ„ํ•ด @Transactional ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ๊ฐ„ํŽธํ•˜๊ฒŒ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌํ•˜์˜€๋‹ค.

DTO

์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์š”์ฒญ ๋ฐ›๊ณ  ๋ฐ˜ํ™˜ ๋˜๋Š” Request์™€ Response๋Š” Presentation Layer์— ์ž๋ฆฌํ–ˆ๋‹ค. ๊ทธ ์™ธ์— Domain ์˜์—ญ์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ค๋ฅธ ๊ณ„์ธต์— ๋„˜๊ธธ ๋•Œ ๊ฐ์‹ธ๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” DTO๋Š” ์ „๋ถ€ Domain ์˜์—ญ์— ์ •์˜๋˜์—ˆ๋‹ค.

๋ฐ์ดํ„ฐ ๊ฒ€์ฆ

ํ‘œํ˜„ ์˜์—ญ์—์„œ๋Š” ๋‹จ์ˆœํžˆ ๊ฐ’์˜ ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ–ˆ๋‹ค๋ฉด, ๋„๋ฉ”์ธ ์„œ๋น„์Šค๋Š” ๊ฐ’์ด ํ•ด๋‹น ๋„๋ฉ”์ธ์— ๋งž๋Š” ์œ ํšจํ•œ ๊ฐ’์ธ์ง€ ๊ฒ€์ฆํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•˜๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด ์ƒ๋…„์›”์ผ ๋‚ ์งœ ์ŠคํŠธ๋ง์ด ์œ ํšจํ•œ ๋‚ ์งœ ๊ฐ’์ธ์ง€ ๊ฒ€์ฆํ•˜๊ฑฐ๋‚˜, ์ถ”๊ฐ€๋กœ 100์‚ด ๋ฏธ๋งŒ์ธ์ง€ ๋“ฑ์„ ๊ฒ€์ฆํ•˜๊ธฐ๋„ ํ•˜์˜€๋‹ค.

	SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
    dateFormat.setLenient(false);
    try {
        dateFormat.parse(userTypeRequest.getBirthday());
    } catch (ParseException e) {
        throw new BadRequestException(ErrorCode.INVALID_BIRTHDAY.getErrorCode());
    }

0๊ฐœ์˜ ๋Œ“๊ธ€