Concept

아래에 사용된 예제는 NodeJS, ExpressJS, MongoDB를 이용하여 구성되었습니다.

References


모놀리식 아키텍처는 소프트웨어 프로그램의 전통적인 모델로, 자체 포함 방식이며 다른 애플리케이션과 독립적인 통합된 유닛으로 만들어집니다. “모놀리스"라는 단어는 거대하고 빙하 같은 것을 의미하는 경우가 많은데, 소프트웨어 설계의 모놀리식 아키텍처도 크게 다르지 않습니다. 모놀리식 아키텍처는 모든 비즈니스 관련 사항을 함께 결합하는 하나의 코드 베이스를 갖춘 대규모의 단일 컴퓨팅 네트워크입니다.

1. 모놀리식 아키텍처 VS 마이크로서비스 아키텍처

마이크로서비스

Monolithic Architecture

전통적인 모놀로식 아키텍처가 비효율적으로 보일 수 있지만 간단한 아키텍처의 경우 모놀리식 아키텍처도 충분히 활용 가능한 솔루션이라고 할 수 있습니다. 시작부터 마이크로 서비스를 고려하는것이 나쁜것은 아니지만 개발중인 애플리케이션이 충분히 복잡하지 않다면 마이크로서비스 아키텍처의 이점을 확인하기는 쉽지 않습니다. 모놀로딕 아키텍처로 시작하여 서비스 확장에 따라 마이크로 서비스 아키텍처로 리팩토링하는것이 효율적인 개발 방법일수도 있습니다.

장점

장점 설명
손쉬운 배포 실행 파일 또는 디렉토리가 하나여서 배포가 더 쉽습니다.
개발 하나의 코드 베이스로 애플리케이션을 구축하여 개발이 더 쉽습니다.
성능 중앙 집중식 코드 베이스 및 리포지토리에서는 대부분 하나의 API만으로 마이크로서비스에서 여러 API가 수행하는 것과 동일한 기능을 수행할 수 있습니다.
테스트 간소화 모놀리식 애플리케이션은 하나의 중앙 집중식 장치이므로 분산된 애플리케이션보다 엔드투엔드 테스트를 더 빠르게 수행할 수 있습니다.
간편한 디버깅 모든 코드가 한 곳에 있으므로 요청을 따라가서 문제를 찾기가 더 쉽습니다.

단점

단점 설명
느린 개발 속도 대규모 모놀리식 애플리케이션에서는 개발이 더욱 복잡해지고 속도가 느려집니다.
확장성 개별 컴포넌트를 확장할 수 없습니다.
안정성 모듈에 오류가 있으면 애플리케이션 전체의 가용성에 영향을 줄 수 있습니다.
기술 채택의 장벽 프레임워크 또는 언어를 변경하면 애플리케이션 전체에 영향을 미치므로 변경 시 비용과 시간이 많이 소요되는 경우가 많습니다.
유연성 부족 모놀리스의 경우 모놀리스에서 이미 사용한 기술로 제한됩니다.
배포 모놀리식 애플리케이션을 약간만 변경하는 경우에도 전체 모놀리스를 다시 배포해야 합니다.

애플리케이션 구조

아래의 코드를 통해 monolithic 구조를 살펴보자. (이 예는 Monolithic 아키텍처를 설명하기 위한것으로 구조화되지 않은것이 Monolithic 아키텍처의 특징으로 오해하면 안된다. )

[monolithic architecture example github] https://github.com/zachgoll/monolithic-architecture-example-app

이 코드에서 확인 가능한것은 애플리케이션간의 구분이 없다는것이다. app.js에서 데이터베이스, 서버 및 API 엔드포인트에 대한 연결을 확인할 수 있다.

const express = require("express");
const app = express();
const mongoose = require("mongoose");
const cors = require("cors");
const bodyParser = require("body-parser");

// This will allow our presentation layer to retrieve data from this API without
// running into cross-origin issues (CORS)
app.use(cors());
app.use(bodyParser.json());

// ============================================
// ==========  DATABASE CONNECTION  ===========
// ============================================
// Connect to running database
mongoose.connect(
  `mongodb://${process.env.DB_USER}:${process.env.DB_PW}@127.0.0.1:27017/monolithic_app_db`,
  { useNewUrlParser: true }
);

// User schema for mongodb
const UserSchema = mongoose.Schema(
  {
    name: { type: String },
    email: { type: String },
  },
  { collection: "users" }
);

// Define the mongoose model for use below in method
const User = mongoose.model("User", UserSchema);

function getUserByEmail(email, callback) {
  try {
    User.findOne({ email: email }, callback);
  } catch (err) {
    callback(err);
  }
}

// set the view engine to ejs
app.set("view engine", "ejs");

// index page
app.get("/", function (req, res) {
  res.render("home");
});

// ============================================
// ============  API ENDPOINT  ================
// ============================================
app.post("/register", function (req, res) {
  const newUser = new User({
    name: req.body.name,
    email: req.body.email,
  });

  newUser.save((err, user) => {
    res.status(200).json(user);
  });
});

// ============================================
// ==============  SERVER =====================
// ============================================
app.listen(8080);
console.log("Visit app at http://localhost:8080");

이 모놀리딕 애플리케이션이 확장하기 시작할 경우 빠르게 코드는 엉망이 될 것이다. 이 단계에서 대부분 마이크로서비스 아키텍처로의 전환을 선택하지만, 리팩토링을 통하여 계층화된 아키텍처로 바꾸는것을 다른 하나의 옵션으로 고민해 볼 수 있다.

Monolithic Architecture (with better “layered” or “n-tier” design)

계층화된 아키텍처는 애플리케이션을 일반적으로 다음과 같은 레이어들로 분할할 수 있다.

  1. 프리젠테이션 계층(Presentation Layer)
  2. 비지니스 계층(Business Layer)
  3. 데이터 액세스 계층(Data Access Layer)

다른 형태로 다음과 같은 레이어로 분류할 수도 있다.

  1. Presentation Layer
  2. Application Layer
  3. Domain Layer
  4. Persistence Layer

Layered Architecture Diagram

flowchart TD subgraph Presentation-Layer direction LR Angular --- A{{Closed}} end subgraph Business-Layer direction LR Express --- B{{Closed}} end subgraph Shared-Utilities-Layer direction LR String-Utilities --- Object-Transformation-Utilities --- C{{Open}} end subgraph Data-Layer direction LR Mongo --- D{{Closed}} end Presentation-Layer --> Business-Layer Business-Layer --> Shared-Utilities-Layer Shared-Utilities-Layer --> Data-Layer click Angular "https://www.github.com" _blank

중요한 점은 각 레이어 구조에서 바로 아래 레이어만 사용할 수 있게 하도록 구조를 분리하는것입니다. 하지만 Utility 레이어의 경우처럼 때로는 공유하여 쓸수 있는 레이어가 필요할 수도 있습니다. 다이어그램에서 모든 레이어에서 사용할 수 있도록 열린 레이어로 생성한것을 확인할 수 있습니다.

Application Structure

위에서 언급한대로 계층화된 아키텍처에서는 각 계층이 바로 아래 계층만 사용할수 있다는 규칙이 있습니다. 그럼 이 중요한 규칙을 기반으로 monolothic archicture를 변경해 보겠습니다.

  1. 프레젠테이션 계층은 HTML 사용자 양식에서 호출합니다.
  2. 프레젠테이션 계층 자바스크립트는 양식을 처리하고 비즈니스 계층에 대한 호출을 실행합니다.
  3. 비즈니스 계층은 양식 정보를 처리하고 데이터 액세스 계층을 호출합니다.
  4. 데이터 액세스 계층은 정보를 처리하고 사용자를 위해 데이터베이스에 쿼리합니다.
  5. 데이터 액세스 계층은 비즈니스 계층에 정보를 반환합니다.
  6. 비즈니스 계층은 HTTP를 통해 프레젠테이션 계층에 정보를 반환합니다.
  7. 프레젠테이션 레이어는 새로운 정보로 뷰를 렌더링합니다.

1. 프레젠테이션 계층은 HTML 사용자 양식에서 호출합니다.

<!-- File: home.ejs -->

<!-- On form submit, home.ejs executes the getDataFromBusinessLayer() function -->

<form id="emailform" onsubmit="getDataFromBusinessLayer()">
  <input name="email" id="email" placeholder="Enter email..." />
  <button type="submit">Load Profile</button>
</form>

2. 프리젠테이션 계층 자바스크립트는 양식을 처리하고 비즈니스 계층에 대한 호출을 실행합니다.

// File: presentation-layer-user.js

function getDataFromBusinessLayer() {
  event.preventDefault();
  const email = $("#email").val();

  // Perform the GET request to the business layer
  // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  $.ajax({
    url: `http://localhost:8081/get-user/${email}`,
    type: "GET",
    success: function (user) {
      // Render the user object on the page
      // Ommitted for brevity
    },
    error: function (jqXHR, textStatus, ex) {
      console.log(textStatus + "," + ex + "," + jqXHR.responseText);
    },
  });
}
  1. 비즈니스 계층은 양식 정보를 처리하고 데이터 액세스 계층을 호출합니다.
// File: business-layer-user.js

app.get("/get-user/:useremail", function (req, res) {
  // Makes a call to the data access layer
  // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  const user = User.getUserByEmail(req.params.useremail, (error, user) => {
    res.status(200).json({
      name: user.name,
      email: user.email,
      profileUrl: user.profileUrl,
    });
  });
});
  1. 데이터 접근 계층은 정보를 처리하고 사용자를 위해 데이터베이스에 쿼리합니다.
// File: data-layer-user.js

module.exports.getUserByEmail = (email, callback) => {
  try {
    // Makes a call to the database
    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    User.findOne({ email: email }, callback);
  } catch (err) {
    callback(err);
  }
};
  1. 데이터 액세스 계층은 비즈니스 계층에 정보를 반환합니다.

  2. 비즈니스 계층은 HTTP를 통해 프레젠테이션 계층에 정보를 반환합니다.

  3. 프레젠테이션 레이어는 새로운 정보로 뷰를 렌더링합니다.

각 단계를 통해 계층이 담당하는 구체적인 의무를