Architecture

์‘์šฉ ์„œ๋น„์Šค๋Š” ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š”๊ฐ€

hyunki.Dev 2023. 3. 25. 01:51

๐Ÿ“Œ ๋“ค์–ด๊ฐ€๋ฉฐ

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

 

๐Ÿ“Œ ์‘์šฉ ์„œ๋น„์Šค๋ž€

 ์‘์šฉ ์„œ๋น„์Šค๋Š” ํ‘œํ˜„ ์˜์—ญ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ์ž์™€ ๋„๋ฉ”์ธ์„ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ๋งค๊ฐœ์ฒด ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ํ‘œํ˜„ ์˜์—ญ์ด ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ URL, ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ, ์ฟ ํ‚ค, ํ—ค๋” ๋“ฑ์„ ์ด์šฉํ•ด์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์–ด๋–ค ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•˜๊ธฐ๋ฅผ ์›ํ•˜๋Š”์ง€ ํŒ๋ณ„ํ•˜๊ณ  ๊ทผ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์‘์šฉ ์„œ๋น„์Šค๋ฅผ ์‹คํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

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

@GetMapping("/test")
public ModelAndView join(HttpServletRequest request){

    String email = request.getParameter("email");
    String password = request.getParameter("password")
    
    //์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์„ ์‘์šฉ ์„œ๋น„์Šค์— ๋งž๊ฒŒ ๋ณ€ํ™˜
    JoinRequest joinReq = new JoinRequest(email, password);
    
    //๋ณ€ํ™˜ํ•œ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด์„œ ์‘์šฉ ์„œ๋น„์Šค๋ฅผ ์‹คํ–‰
    joinService.join(joinReq);
    
    ...
}

 

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

 

๋”ฐ๋ผ์„œ ์‘์šฉ  ์„œ๋น„์Šค๋Š” ์ฃผ๋กœ ๋„๋ฉ”์ธ ๊ฐ์ฒด ๊ฐ„์˜ ํ๋ฆ„์„ ์ œ์–ดํ•˜๋Š” ๋กœ์ง๋“ค์ด ์žˆ๊ฒŒ ๋˜๋ฉฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•œ๋ˆˆ์— ํ๋ฆ„์„ ๋ณผ ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋กœ ๊ฐœ๋ฐœ๋ฉ๋‹ˆ๋‹ค.

public Response doFunc(Request req){

    //1. ๋ ˆํฌ์ง€ํ„ฐ๋ฆฌ๋กœ๋ถ€ํ„ฐ ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ๋ฅผ ๊ตฌํ•œ๋‹ค.
    Aggregate agg = repository.findById(req.getId());
    checkNull(agg)
    
    //2. ์• ๊ทธ๋ฆฌ๊ฑฐํŠธ์˜ ๋„๋ฉ”์ธ ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•œ๋‹ค.
    agg.doSomething(req.getValue());
    
    //3. ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.
    return createSuccessfulResponse(agg);
}

 

๐Ÿ“Œ ์‘์šฉ ์„œ๋น„์Šค  ๊ตฌํ˜„ ๋ฐฉ์‹

๋„๋ฉ”์ธ ๋กœ์ง ๋„ฃ์ง€ ์•Š๊ธฐ 

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

 

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

 

์ ์ •ํ•œ ์‘์šฉ ์„œ๋น„์Šค์˜ ํฌ๊ธฐ

์‘์šฉ ์„œ๋น„์Šค๋Š” ๋ณดํ†ต ๋‹ค์Œ์˜ ๋‘๊ฐ€์ง€ ์ค‘ ํ•˜๋‚˜์˜ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

- ํ•œ ์‘์šฉ ์„œ๋น„์Šค ํด๋ž˜์Šค์— ๋„๋ฉ”์ธ์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ

- ๊ตฌ๋ถ„๋˜๋Š” ๊ธฐ๋Šฅ๋ณ„๋กœ ์‘์šฉ ์„œ๋น„์Šค ํด๋ž˜์Šค๋ฅผ ๋”ฐ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ

 

 ํ•œ ์‘์šฉ ์„œ๋น„์Šคํด๋ž˜์Šค์— ๋„๋ฉ”์ธ์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•œ๋‹ค๋ฉด ํ•œ ๋„๋ฉ”์ธ๊ณผ ๊ด€๋ จ๋œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ๊ฐ€ ํ•œ  ํด๋ž˜์Šค์— ์œ„์น˜ํ•˜๋ฏ€๋กœ ๊ฐ ๊ธฐ๋Šฅ์—์„œ ๋™์ผ ๋กœ์ง์— ๋Œ€ํ•œ ์ฝ”๋“œ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํ•œ ์„œ๋น„์Šค ํด๋ž˜์Šค์˜ ์ฝ”๋“œ ์ค„ ์ˆ˜๊ฐ€ ์ปค์ง„๋‹ค๋Š” ๋‹จ์ ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ ํฌ๊ธฐ๊ฐ€ ์ปค์ง€๊ฒŒ ๋œ๋‹ค๋ฉด ์—ฐ๊ด€์„ฑ์ด ์ ์€ ์ฝ”๋“œ๊ฐ€ ํ•œ ํด๋ž˜์Šค์— ํ•จ๊ป˜ ์œ„์น˜ํ•˜๊ฒŒ ๋˜์–ด ๊ฒฐ๊ณผ์ ์œผ๋กœ ๊ด€๋ จ ์—†๋Š” ์ฝ”๋“œ๊ฐ€ ๋’ค์„ž์—ฌ ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. 

 

๊ตฌ๋ถ„๋˜๋Š” ๊ธฐ๋Šฅ๋ณ„๋กœ ์„œ๋น„์Šค ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ์‹์€ ํ•œ ์‘์šฉ ์„œ๋น„์Šค ํด๋ž˜์Šค์—์„œ ํ•œ ๊ฐœ ํ˜น์€ 2-3๊ฐœ์˜ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ์ฟ ํฐ ๋„๋ฉ”์ธ์ด ์žˆ๋‹ค๊ณ  ์นœ๋‹ค๋ฉด CouponUseService(์ฟ ํฐ ์‚ฌ์šฉ ๊ธฐ๋Šฅ), CouponIssueService(์ฟ ํฐ ๋ฐœ๊ธ‰ ๊ธฐ๋Šฅ), CouponHistoryService(์ฟ ํฐ ์ด๋ ฅ ์ €์žฅ) ๋“ฑ์œผ๋กœ ํด๋ž˜์Šค๋ฅผ ์—ฌ๋Ÿฌ๊ฐœ ๋งŒ๋“ค์–ด ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ํด๋ž˜์Šค๋Š” ๋งŽ์•„์ง€์ง€๋งŒ ํ•œ ํด๋ž˜์Šค์— ๊ด€๋ จ ๊ธฐ๋Šฅ์„ ๋ชจ๋‘ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ๊ณผ ๋น„๊ตํ–ˆ์„ ๋•Œ ์ฝ”๋“œ ํ’ˆ์งˆ์„ ์ผ์ • ์ˆ˜์ค€์œผ๋กœ ์œ ์ง€ํ•˜๋Š”๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. 

 

๊ฐ ๊ธฐ๋Šฅ๋งˆ๋‹ค ๋™์ผํ•œ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜์—ฌ ์ฝ”๋“œ๊ฐ€ ์ค‘๋ณต๋˜๋Š” ๊ฒฝ์šฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ„๋„ ํด๋ž˜์Šค์— ๋กœ์ง์„ ๊ตฌํ˜„ํ•ด์„œ ์ฝ”๋“œ ์ค‘๋ณต์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// ๊ฐ ์‘์šฉ ์„œ๋น„์Šค์—์„œ ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉ๋  ๋กœ์ง์„ ๋ณ„๋„ ํด๋ž˜์Šค๋กœ ๊ตฌํ˜„

public final class MemberServiceHelper {
  
  public static Member findExistMember(MemberRepository repository, String memberId){
  
    Member member = memberRepository.findById(memberId);
    if(member == null) throw new NoMemberException(memberId);
    return member;
  
  }

}

//์‘์šฉ ์„œ๋น„์Šค์—์„œ ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ตฌํ˜„
import static com.myshop.member.application.MemberServiceHelper.*;

public class ChangePasswordService{

  private MemberRepository memberRepository;
  
  public void changePassword(String memberId, String curPw, String newPw){
    
    Member member = findExistingMember(memberRepository, memberId);
    member.changePassword(currPw, mewPw);
  
  }

}

 

 

์‘์šฉ ์„œ๋น„์Šค์˜ ๊ฐ’ ๋ฆฌํ„ด

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

 

 

์ฐธ๊ณ : DDD START! ๋„๋ฉ”์ธ ์ฃผ๋„ ์„ค๊ณ„ ๊ตฌํ˜„๊ณผ ํ•ต์‹ฌ ๊ฐœ๋… ์ตํžˆ๊ธฐ