노드 REST API에서 컨트롤러와 서비스를 분리해야 하는 이유는 무엇입니까?

16530 단어 javascriptnode
최초 발표coreycleary.me.이것은 나의 콘텐츠 블로그의 교차 게시물이다.나는 1, 2주에 한 번씩 새로운 내용을 발표한다. 만약 당신이 직접 나의 글을 당신의 우편함에 보내고 싶다면, 당신은 sign up to my newsletter할 수 있다.나는 또 자주 메모지와 기타 무료 경품을 발송한다.
이것은 나의 이전 문장의 후속 내용이다.그 글에서 우리는 양자 간의 차이, 그리고 어떤 논리가 어디에 적용되는지 토론했지만, 당신이 이렇게 하기를 원할 수 있는 이유만 간단하게 언급했다.
너는 아마도 아직도 생각하고 있을 것이다. "왜 둘을 나누는 것이 좋은 생각입니까?"컨트롤러가 이미 작동할 때 왜 서비스를 사용합니까?
왜 우리가 이 문장에서 더욱 깊이 있게 탐구할 문제인가.
디렉터만 사용
간단한 루트와 컨트롤러 몇 개만 있고, 업무 논리를 서비스에 도입하지 않았다면, 현재의 구조에 대해 너무 화를 내지 않았을 수도 있습니다.명확한 것은 우리가 토론한 것은 프로젝트의 서비스 파일이지 단독 REST 서비스가 아니다.
그러나 만약 당신의 응용 프로그램이 이 정도까지 발전했다면, 나는 당신이 이미 다음과 같은 몇 가지 고통을 겪었다고 확신한다.
  • 코드가 많은 컨트롤러가 있는데 많은 일을 한다. 바로'뚱뚱 컨트롤러'다.
  • 이전 코드와 밀접한 관계가 있어서 코드가 혼란스러워 보입니다.만약 컨트롤러가 4회 또는 5회 이상의 데이터베이스/모델 호출을 진행하면 그에 따른 오류를 처리할 수 있다. 그러면 코드는 보기 흉해 보일 수 있다.
  • 너는 심지어 어디서부터 테스트를 쓰기 시작했는지도 모른다.
  • 수요에 변화가 생기거나 새로운 기능을 추가해야 하기 때문에 재구성하기가 매우 어렵다.
  • 코드의 재사용은 거의 존재하지 않는다.
  • 이별은 무슨 도움이 됩니까?
    이 주제에 관한 이전 글을 반복합니다. 컨트롤러와 서비스에서 분리해야 할 것은 업무 논리와 웹/HTTP 논리입니다.
    그래서 컨트롤러는 HTTP 요청에서 필요한 데이터를 추출하고 (만약 당신이 Express를 사용한다면, 그것은 req 대상) 데이터를 어느 서비스로 이동해야 하는지를 결정하는 기본적인 일을 처리할 것이다.물론 결국 답장할 것이다.
    이러한 서비스는 데이터베이스 호출, 데이터 처리, 포맷, 업무 규칙에 따라 알고리즘 처리 등 복잡한 업무를 책임지지만, 이러한 업무는 HTTP층에 특정된 것이 아니라 귀하의 업무 영역에 특정된 것입니다.
    이별 후 이 같은 고통은 완전히 사라지지 않아도 크게 줄어든다.이것이 바로 서비스 사용의 묘미이다.네, 항상 재구성되고 테스트하기 어려운 것이 있지만, 이 물건들을 서비스에 넣으면 더욱 쉽습니다.
    그게 왜?
    이 고통들을 하나하나 되돌아봅시다.다음은 이전 기사에서 나온 모든 논리가 컨트롤러에 있는 코드 예입니다.
    const registerUser = async (req, res, next) => {
      const {userName, userEmail} = req.body
      try {
        // add user to database
        const client = new Client(getConnection())
        await client.connect()
    
        await client.query(`INSERT INTO users (userName) VALUES ('${userName}');`)
        await client.end()
    
        // send registration confirmation email to user
        const ses = new aws.SES()
    
        const params = { 
          Source: sender, 
          Destination: { 
            ToAddresses: [
              `${userEmail}` 
            ],
          },
          Message: {
          Subject: {
            Data: subject,
            Charset: charset
          },
          Body: {
            Text: {
              Data: body_text,
              Charset: charset 
            },
            Html: {
              Data: body_html,
              Charset: charset
            }
          }
        }
    
        await ses.sendEmail(params) 
    
        res.sendStatus(201)
        next()
      } catch(e) {
        console.log(e.message)
        res.sendStatus(500) && next(error)
      }
    }
    
    코드가 풍부하고 복잡한 컨트롤러 - 일명'뚱뚱 컨트롤러'
    너는 아마도 지방 컨트롤러라는 단어를 들어 본 적이 있을 것이다.컨트롤러에 코드가 너무 많을 때, 그것은 응, 뚱뚱해 보인다.
    이것은 분명히 코드를 읽고 이해하는 것을 더욱 어렵게 한다.길고 복잡한 코드를 가지는 것은 때때로 피할 수 없는 일이지만, 우리는 이 코드들이 고립되고 일반적인 일을 책임지기를 바란다.
    컨트롤러는 여러 가지 다른 것들을 조화시켜야 하기 때문에, 만약 당신이 이 다른 것들을 서비스에 끌어들이지 않는다면, 컨트롤러에 포함된 코드의 양을 증가시킬 것이다.
    업무 논리를 서비스에 끌어들여 컨트롤러가 쉽게 읽을 수 있게 되었다.서비스 재구축을 사용한 위의 코드 버전을 살펴보겠습니다.
    컨트롤러 단순화:
    const {addUser} = require('./registration-service')
    const {sendEmail} = require('./email-service')
    
    const registerUser = async (req, res, next) => {
      const {userName, userEmail} = req.body
      try {
        // add user to database
        await addUser(userName)
    
        // send registration confirmation email to user
        await sendEmail(userEmail)
    
        res.sendStatus(201)
        next()
      } catch(e) {
        console.log(e.message)
        res.sendStatus(500) && next(error)
      }
    }
    
    module.exports = {
      registerUser
    }
    
    등록 서비스:
    const addUser = async (userName) => {
      const client = new Client(getConnection())
      await client.connect()
    
      await client.query(`INSERT INTO users (userName) VALUES ('${userName}');`)
      await client.end()
    }
    
    module.exports = {
      addUser
    }
    
    이메일 서비스:
    const ses = new aws.SES()
    
    const sendEmail = async (userEmail) => {
      const params = { 
        Source: sender, 
        Destination: { 
          ToAddresses: [
            `${userEmail}`
          ],
        },
        Message: {
          Subject: {
            Data: subject,
            Charset: charset
          },
          Body: {
            Text: {
              Data: body_text,
              Charset: charset 
            },
            Html: {
              Data: body_html,
              Charset: charset
            }
          }
        }
      }
    
      await ses.sendEmail(params) 
    }
    
    module.exports = {
      sendEmail
    }
    
    이제 우리는 무슨 일이 일어났는지 더 쉽게 이해할 수 있는 마른 컨트롤러가 생겼다.
    코드 재사용 불가
    또 다른 큰 문제는 코드를 다시 사용할 수 없다는 것이다.예를 들어 우리는 다른 지역의 다른 컨트롤러에서 같은 전자메일 발송 코드를 사용하고 싶다. 아마도 API 루트를 지원하는 컨트롤러일 것이다. 이 컨트롤러는 Reddit 스타일의 포럼에서 전자메일을 보내서 후속 논평을 얻을 수 있을 것이다.
    우리는 이 코드들을 복사해서 다른 종류의 전자메일을 보낼 수 있는 충분한 전자메일 서비스만 만들어서 이 서비스를 필요로 하는 모든 컨트롤러에 가져오는 것이 아니라 조정해야 한다.
    재구성하기 어렵다
    상술한 두 가지 문제에 이어 우리가 업무 논리를 서비스로 분리하지 않았을 때, 새로운 기능을 재구성하거나 추가하는 것은 더욱 어려워진다.
    만약 코드가 뒤죽박죽이고 비대해진다면, 의외로 부근의 다른 코드를 파괴하지 않은 상황에서 재구성하는 것은 훨씬 어려울 것이다.이것은 더욱 뚜렷한 것이다.
    그러나 만약 우리가 새로운 특성이나 기능을 추가해야 한다면?만약에 우리가 지금 두 개의 컨트롤러가 있는데 모두 어떤 사건이 발생한 후에 이메일을 보낸다(사용자 등록, 사용자가 댓글에 후속 댓글을 받는 등).만약 우리가 매우 비슷한 두 단락의 전자 우편 코드를 가지고 있다면, 우리는 전자 우편 공급자 (예를 들어 AWS에서Sendgrid까지) 를 바꾸고 싶다.우리는 지금 반드시 두 곳에서 변화를 해야 한다.두 곳의 테스트를 바꿨다.
    테스트 작성 어려움
    마지막으로, 이것은 큰 문제입니다. 서비스를 사용하지 않을 때, 포함하고자 하는 논리적 컴파일 테스트가 더욱 어려워집니다.
    컨트롤러에 여러 개의 다른 논리 세션이 포함되어 있을 때, 반드시 여러 개의 코드 경로를 덮어써야 한다.나는 심지어 위에서 제한된 컨트롤러의 예시를 어디서부터 테스트하기 시작했는지 모른다.많은 일을 해야 하기 때문에, 우리는 모든 일을 고립적으로 테스트할 수 없다.
    그러나 코드가 더욱 고립되면 테스트가 더욱 쉬워진다.
    서비스에 대해 HTTP 요청 대상이나 웹 프레임워크를 처리할 필요가 없습니다.그래서 우리의 테스트는 이 점을 고려할 필요가 없다.우리는 req 과/또는 res 대상을 모방할 필요가 없다.
    일단 업무 논리가 서비스에 도입되고 귀하가 이 서비스에 대한 테스트를 작성했기 때문에 컨트롤러 자체에 대한 테스트가 필요하지 않을 수도 있다고 생각합니다.요청의 경로를 어느 서비스로 할지 결정하는 논리가 있다면, 테스트를 해야 할 수도 있습니다.단, 이 점을 테스트하기 위해 supertest 일부 끝에서 끝까지의 테스트를 작성할 수도 있습니다. API 루트를 호출하기만 하면 정확한 응답을 받을 수 있습니다.
    마무리
    그러면 컨트롤러부터 시작해서 업무 논리를 서비스로 끌어올려야 합니까?아니면 처음부터 시작할까요?컨트롤러를 추가해야 하는 모든 항목/새로운 기능을 시작하여 컨트롤러와 서비스로 나누는 것이 제 조언입니다.이것은 내가 모든 응용 프로그램을 처리하는 방식이다.
    만약 이미 한 응용 프로그램이 서비스를 사용하지 않는다면, 추가해야 할 모든 새로운 기능에 대해, 새로운 루트/컨트롤러라면 서비스 방법부터 시작하십시오.만약 새로운 컨트롤러가 필요하지 않다면, 기존의 컨트롤러를 사용 서비스로 재구성해 보세요.
    장기적으로 보면 위에서 논의한 모든 이유로 인해 당신은 자신을 훨씬 쉽게 만들 수 있을 뿐만 아니라, 이러한 방식으로 프로젝트 구축을 연습하는 데 익숙해질 것이다.
    Node와 JavaScript가 쉽게 이해할 수 있도록 많은 새 컨텐츠를 작성하고 있습니다.더 간단하다. 왜냐하면 나는 그것이 때처럼 복잡해야 한다고 생각하지 않기 때문이다.만약 당신이 이 글을 좋아하고 나의 시사통신here's that link again을 구독하는 것이 매우 도움이 된다고 생각한다면!

    좋은 웹페이지 즐겨찾기