Heroku로 신의 서비스를 파괴하다


지난 10년 동안 나는 '응용 프로그램 현대화 계획' 으로 분류되는 프로젝트에 참여해 왔다이러한 노력의 목표는 업데이트된 프레임워크, 디자인 모델, 언어를 사용하여 남겨진 응용 프로그램이나 서비스를 대체하는 것이다.
각 상황에서 다음 세 가지 교훈 중 적어도 두 가지가 옳다고 증명되었다.
  • 현대화 서비스로 위장한 다른 전체로 전체를 바꾸지 마라.
  • 현대화 시스템에 포함된 불량 데이터 모델을 받아들이는 것을 피한다.
  • 기존의 프로그램 논리가 100% 정확하다고 영원히 가정하지 마라.
  • 이것은 나로 하여금 몇 년 역사가 있는 가죽 카드를 한 대 샀다는 것을 한 번 생각나게 했다.나는 나의 구매를 자랑스럽게 여긴다. 이 트럭을 운전하는 것은 매우 재미있다.사실 나는 아버지께 트럭을 개선하고 싶다고 말씀드렸다.너는 이러한 변화가 그것을 매우 아름답게 보일 뿐만 아니라, 심지어는 개선된 음향 시스템도 증가시킬 수 있다는 것을 안다.
    나의 아버지는 나의 웅대한 계획을 흥미진진하게 듣고 계신다.내가 완성했을 때, 그는 내가 이 모든 일을 할 수 있다는 것을 확인했다.그러나 결국 그는 나에게 "여전히 낡은 트럭 한 대가 있을 것"이라고 말했다
    그의 관점은 매우 명확하다.나는 트럭이 더 좋아 보일 수 있도록 많은 자금을 투입할 수 있지만, 만약 내가 기초 부품을 교체하지 않는다면, 나는 여전히 같은 트럭을 가지고 있을 것이다.이것은 내가 차주와 그들의 낡은 차량이 자주 겪는 도전에 직면해야 한다는 것을 의미한다.
    나는 많은'응용 프로그램 현대화'프로젝트가 곧 보수적인 결정을 내릴 것이고 결국 이런'새로운'해결 방안이 그렇게 참신하고 개선되지 않을 것이라고 생각한다.나의'낡은 트럭'예시와 같이 이런 남겨진 디자인 결정이 새로운 응용 프로그램에 도전을 가져오기 시작한 것은 시간문제일 뿐이다.
    이 세 가지 경험과 교훈 중 하나는 모두 단독 출판물의 주제가 될 수 있다.본고에서 나는 전통적인 monolith 응용 프로그램을 다른 현대화 응용 프로그램으로 위장한 monolith로 대체하는 것을 어떻게 피해야 하는지를 중점적으로 소개할 것이다.

    일이 궤도에서 벗어난 곳


    고객이 주문서를 제출할 수 있도록 매우 간단한 비즈니스 해결 방안을 고려하다.원래 응용 프로그램에는 세 개의 테이블이 있는 데이터베이스가 있습니다.

    CUSTOMERS 테이블은 고객 정보를 유지합니다.테이블의 ID 열은 ORDERS 테이블에 연결되어 주문을 고객과 일치시킵니다.고객의 ID 열도 결제 양식에 연결됩니다.
    일반적으로 모든 내용을 데이터베이스에 저장하기로 결정합니다.이것은 같은 테이블 사이의 상호작용을 하는 구성 요소와 서비스를 포함하는 새로운 서비스를 만들 것입니다.
    다음 그림은 단일 응용 프로그램만 RESTful API로 대체하는 디자인을 보여 줍니다.이 작업으로 인해 데이터베이스는 변하지 않는다.

    비록 이 생각은 이론적으로 듣기에는 좋지만, 이것은 통상적으로 새로운 서비스를 만들어 낸다. 그것은 원시 시스템처럼 복잡하다. 만약 더 복잡하지 않다면.이것이 바로 통상적으로 말하는 신의 서비스다.
    문제를 더욱 복잡하게 만드는 것은 고객의 요구를 충족시키기 위해 모든 API(위에서 말한 바와 같이)를 동기화하여 축소해야 한다는 것이다.기본 설계에 따라 확장 옵션은 수직 확장에만 한정될 수 있으며 이는'문제시 하드웨어 던지기'솔루션을 호출할 수 있다.
    현대화된 서비스로 위장한 또 다른 큰 바위로 큰 바위를 교체하지 말라는 교훈이 검증된 곳이다.

    일을 더 잘하다


    같은 예를 사용해서 생각해 보자. 만약에 응용 프로그램의 현대화 계획이 다음과 같은 디자인을 사용했다면 어떻게 해야 합니까?

    이러한 설계를 통해 응용 프로그램의 영역 소유권을 수용하기 위해 세 가지 새로운 마이크로 서비스를 도입할 것이다.가장 중요한 것은 모든 마이크로 서비스와 그 전용 대상은 자신의 데이터베이스를 가지고 있다는 것이다.
    이러한 서비스 간의 연결은 메시지 전달 서비스를 이용하고 보통 요청-응답 모드를 사용한다.
    예를 들어 주문서의 용례를 고려해 보자.주문 사용 메시지는 알려진 고객 데이터를 고객 API에 제공합니다.Customer API는 요청을 처리하고 기존 CustomerTo(DTO는 데이터 전송 객체)로 돌아가거나 원래 요청에 대한 응답을 통해 새 CustomerTo를 만듭니다.
    그런 다음 고객의 식별자는 주문 및 결제 API를 사용한 결제 요청과 연관될 수 있습니다.여기서는 동일한 패턴을 따르지만 고객 API 요청에서 얻은 정보를 활용합니다.
    결제 API 응답 후 새 주문은 해당 데이터베이스에 저장되고 요청한 고객이 확인할 수 있습니다.
    모든 마이크로 서비스는 독립적이기 때문에 고객의 수요를 만족시키는 상하 확장은 현재 식별이 비교적 높거나 낮은) 요청 수준의 서비스와 분리된다.

    Heroku를 이용해서 신의 서비스를 피하세요.


    히로쿠에서 만드는 데 필요한 패턴이 얼마나 쉬운지 보고 싶어요.몇 분 동안 저는 Heroku에 세 개의 응용 프로그램을 만들어서 다음과 같은 설계를 모의했습니다.

    이 세 서비스 중 하나는 자신의 Heroku Postgres 데이터베이스와 Spring 안내 서비스를 포함한다.CloudAMQP (RabbitMQ) 서비스는 jvc 주문 프로그램에 추가되어 이 예시를 가능한 한 간단하게 합니다.WSO2는 이 문서의 API 섹션에 없습니다.
    Heroku 대시보드의 세 응용 프로그램은 다음과 같습니다.

    샘플 테이블 생성하기


    다음 SQL은 기본 테이블을 작성하는 데 사용됩니다.이러한 서비스를 검증하는 데 사용할 수 있는 기능은 다음과 같습니다.
    CREATE TABLE orders (
      id INT PRIMARY KEY NOT NULL,
      customer_id INT NOT NULL,
      payment_id INT NOT NULL,
      order_date timestamp NOT NULL,
      description VARCHAR(255) 
    );
    ​
    CREATE TABLE customers (
      id INT PRIMARY KEY NOT NULL,
      email_address VARCHAR(255) NOT NULL,
      name VARCHAR(255),
      UNIQUE (email_address)
    );
    ​
    CREATE TABLE payments (
      id INT PRIMARY KEY NOT NULL,
      transaction_id VARCHAR(36) NOT NULL,
      amount DECIMAL(12,2),
      customer_id INT NOT NULL
    );
    
    각 CREATE TABLE 명령은 해당 마이크로 서비스와 연관된 PostgreSQL 데이터베이스에 대해 실행된다는 점에 유의하십시오.

    주문 요청 처리


    다음 OrderRequest 로드를 고려하십시오.
    {
       "description" : "Sample Order #4",
       "emailAddress" : "[email protected]",
       "name" : "Brian Johnson",
       "amount" : 19.99
    }
    
    일반적인 상황에서 진정한 주문서는 다른 몇 가지 속성을 포함하지만 목표는'간단함을 유지하는 방법'에 따라 디자인 원칙에 주목하는 것이다.
    주문서의 일부로서 시스템은 주문서를 내린 고객의 표지부와 요청한 거래를 알아야 한다.

    요청 고객


    고객 정보를 요청하기 위해 다음 CustomerTo 로드를 요청 대기열에 배치할 수 있습니다.
    {
       "emailAddress" : "[email protected]",
       "name" : "Brian Johnson"
    }
    
    Order API에서는 Heroku의 Cloud AMQP, 직접 교환의 개념 및 spring boot의 spring boot starter AMQP를 활용합니다.
    public CustomerDto getCustomer(String emailAddress, String name) {
        CustomerDto customerDto = new CustomerDto();
        customerDto.setEmailAddress(emailAddress);
        customerDto.setName(name);
    ​
        return rabbitTemplate.convertSendAndReceiveAsType(customerDirectExchange.getName(),
                messagingConfigurationProperties.getCustomerRoutingKey(),
                customerDto,
                new ParameterizedTypeReference<>() {});
    }
    
    이 예에서 이 요청은 차단 요청입니다. 이것은 주문 API의 처리가 고객 API가 응답할 때까지 기다린다는 것을 의미합니다.
    Customer API에서 customerDirectExchange에서 요청을 기다리는 탐지기가 있습니다.
    @RabbitListener(queues = "#{messagingConfigurationProperties.customerRequestQueue}")
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public CustomerDto receive(CustomerDto customerDto) {
        log.debug("CustomerProcessor: receive(customerDto={})", customerDto);
    ​
        Customer customer = customerRepository.findByEmailAddressEquals(customerDto.getEmailAddress());
    ​
        if (customer != null) {
            log.debug("Found existing customer={}", customer);
            // return customer as a CustomerDto
        } else {
            log.info("Creating new customer={}", customerDto);
            // return new customer as a CustomerDto
        }
    ​
        log.debug("customerDto={}", customerDto);
        return customerDto;
    }
    
    이 예에서는 CustomerTo 객체에 다음과 같은 정보가 포함됩니다.
    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    public class CustomerDto {
        private int id;
        private String emailAddress;
        private String Name;
    }
    

    지불을 요구하다


    PaymentDto를 사용하면 동일한 패턴으로 결제를 요청할 수 있습니다.
    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    public class PaymentDto {
        private int id;
        private String transactionId;
        private BigDecimal amount;
        private int customerId;
    }
    
    customerId 속성은 요청/응답 모드의 결과입니다.물론 지불 API가 처리를 완료하기 전에 id 속성을 설정하지 않습니다. 이 API는 또 하나의 간단한 지불 예시를 사용했습니다.
    @RabbitListener(queues = "#{messagingConfigurationProperties.paymentRequestQueue}")
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public PaymentDto receive(PaymentDto paymentDto) {
        log.debug("PaymentProcessor: receive(paymentDto={})", paymentDto);
    ​
        Payment payment = new Payment();
        payment.setAmount(paymentDto.getAmount());
        payment.setCustomerId(paymentDto.getCustomerId());
        payment.setTransactionId(UUID.randomUUID().toString());
        paymentRepository.save(payment);
        paymentDto.setId(payment.getId());
        paymentDto.setTransactionId(payment.getTransactionId());
    
        return paymentDto;
    }
    

    주문 제출 및 거래 완료


    거래를 완료하면 Postman 클라이언트 또는 간단한 cURL 명령을 사용하여 주문 프로세스를 완료할 수 있습니다.
    curl --location --request POST 'https://jvc-order.herokuapp.com/orders' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "description" : "Sample Order #4",
        "emailAddress" : "[email protected]",
        "name" : "Brian Johnson",
        "amount" : 19.99
    }'
    
    Order API는 POST 요청을 수락하고 HTTP 201(생성됨) 상태와 다음 유효 로드를 반환합니다.
    {
       "id": 4,
       "customerId": 4,
       "paymentId": 4,
       "orderDate": "2021-06-07T04:31:52.497082",
       "description": "Sample Order #4"
    }
    
    세 개의 마이크로 서비스 중 하나는 모두 표준적인 RESTful API를 가지고 있어 완전한 유효 부하 데이터 결과를 검색할 수 있다.
    다음은 Customer API 호출의 예입니다.GET https://jvc-customer.herokuapp.com/customers/4그러면 다음 유효 로드 및 HTTP 200(OK) 상태가 반환됩니다.
    {
        "id": 4,
        "emailAddress": "[email protected]",
        "name": "Brian Johnson"
    }
    
    다음은 결제 API 호출의 예입니다.GET https://jvc-payment.herokuapp.com/payments/4이렇게 하면 HTTP 200(OK) 상태와 다음 유효 부하가 반환됩니다.
    {
        "id": 4,
        "transactionId": "3fcb379e-cb89-4013-a141-c6fad4b55f6b",
        "amount": 19.99,
        "customerId": 4
    }
    
    마지막으로 다음은 Order API 호출의 예입니다.GET https://jvc-order.herokuapp.com/orders/4HTTP 200(OK) 상태로 돌아가며 다음 부하가 포함됩니다.
    {
       "id": 4,
       "customerId": 4,
       "paymentId": 4,
       "orderDate": "2021-06-07T04:31:52.497082",
       "description": "Sample Order #4"
    }
    

    결론


    2021년부터 저는 다음과 같은 IT 전문가에게 적용되는 사명 선언에 주목했습니다.

    “Focus your time on delivering features/functionality which extends the value of your intellectual property. Leverage frameworks, products, and services for everything else.”

    • J. Vester

    히로쿠 생태계는 사명 선언 준수를 쉬워지게 한다.몇 시간 동안 나는 Spring Boot RESTful API와 Heroku Postgres 데이터베이스를 포함하는 세 개의 마이크로 서비스를 완전히 구축하고 원형화했다.클라우드 AMQP는 솔루션에 추가, 통합되고 동일한 시간 내에 검증됩니다.
    나는 만약 내가 표준 클라우드 서비스 제공자를 사용한다면 얼마나 오래 걸릴지 상상할 수 없다.PostgreSQL 데이터베이스와 클라우드 기반의 AMQP 실례를 연결하는 능력, 그리고 처리 권한을 더하면 나의 모든 사용 가능한 시간을 소모할 것이다. 이 기능을 증명할 시간이 없다.
    프로젝트의 실제 소스 코드에 관심이 있으면 GitLab에서 다음 저장소를 참조하십시오.
    jvc-customer
    jvc-order
    jvc-payment
    오늘 하루 즐겁게 보내세요!

    좋은 웹페이지 즐겨찾기