일반 읽기 모델 API 탐색
18931 단어 webdevrubydevjournalprogramming
우리는 그것들을 읽기 모델이라고 부릅니다.
가장 간단한 읽기 모델은 다음으로 구성됩니다.
오늘은 읽기 모델을 만들려고 했습니다. 고객에게 제공되는 제품의 공개 제안.
관리자/판매 패널에서 사용되는 읽기 모델
Products
이 이미 있습니다.처음에는 거의 같은 것 같고 그냥 재사용하고 싶은 유혹이 듭니다.
이것은 CQRS에서 종종 유혹입니다. 이러한 유혹은 CRUD 시스템에서 명백합니다. 제품을 검색하는 한 가지 방법이 있습니다. 얼마나 복잡할까요?
그러나 간단한 기능 세트에서도 서로 다른 열을 포함하고 있음을 이미 볼 수 있습니다.
그들은 다른 필요를 충족시킵니다.
그것을 재사용함으로써 나는 정말로 무엇을 절약하고 있는가?
유일한 위험은 가격을 표시하기 위해 몇 가지 계산을 시작하는 경우(가능성이 매우 높지만 읽기 모델의 일부입니까?) 일부를 잊어버릴 수 있다는 것입니다.
그래도.
현재 읽기 모델을 살펴보았습니다.
module Products
class Product < ApplicationRecord
self.table_name = "products"
end
class Configuration
def call(cqrs)
cqrs.subscribe(
-> (event) { register_product(event) },
[ProductCatalog::ProductRegistered]
)
cqrs.subscribe(
->(event) { change_stock_level(event) },
[Inventory::StockLevelChanged]
)
cqrs.subscribe(
-> (event) { set_price(event) },
[Pricing::PriceSet])
cqrs.subscribe(
-> (event) { set_vat_rate(event) },
[Taxes::VatRateSet])
end
private
def register_product(event)
Product.create(id: event.data.fetch(:product_id), name: event.data.fetch(:name))
end
def set_price(event)
find(event.data.fetch(:product_id)).update_attribute(:price, event.data.fetch(:price))
end
def set_vat_rate(event)
find(event.data.fetch(:product_id)).update_attribute(:vat_rate_code, event.data.fetch(:vat_rate).fetch(:code))
end
def change_stock_level(event)
find(event.data.fetch(:product_id)).update_attribute(:stock_level, event.data.fetch(:stock_level))
end
def find(product_id)
Product.where(id: product_id).first
end
end
end
나는 당신에 관한 것이 아니지만 그것은 나에게 비명을 지른다.
THIS CODE IS MOSTLY DATA
기본적으로 약간의 미묘함이 있는 매핑 선언입니다. 모두 ActiveRecord 위에 있습니다.
db 스키마를 보여주기 위해:
create_table "products", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "name"
t.decimal "price", precision: 8, scale: 2
t.integer "stock_level"
t.datetime "registered_at", precision: nil
t.string "vat_rate_code"
end
(그리고
registered_at
열이 어떻게든 마법처럼 사용되었는지 또는 삭제할 수 있는지 확인하기 위해 TODO에 추가하고 있습니다.)그런 다음 테스트 방법을 살펴 보았습니다.
우리는 도메인 수준에서 거의 100% 돌연변이 적용 범위를 유지하지만 앱 수준(컨트롤러, 읽기 모델)에서는 유사한 품질을 공유하지 않습니다.
이 읽기 모델에는 단위 테스트가 없다는 것을 알았습니다. 통합 테스트를 통해서만 테스트됩니다. 그거 슬프다. 나는 이것을 개선할 필요가 있다.
그러나 두 번째 생각.
일부 기계적 리팩토링(전체 테스트 범위 없이 수행할 수 있는 것)을 수행하면 이 코드는 대부분 선언 또는 일종의 DSL API가 됩니다.
실제로 발생하면 사용 위치 대신 추출되는 결과 "프레임워크"가 필요합니다.
선언적 코드 테스트의 문제는 일반적으로 프로덕션 코드에 있는 내용을 반복하는 "typotest"라는 것입니다.
간단히 말해, 이것은 코드를 더 선언적으로 만들기 위한 첫 번째 시도입니다.
module Products
class Product < ApplicationRecord
self.table_name = "products"
end
class Configuration
def initialize(cqrs)
@cqrs = cqrs
end
def call
@cqrs.subscribe(-> (event) { register_product(event) }, [ProductCatalog::ProductRegistered])
copy(Inventory::StockLevelChanged, :stock_level)
copy(Pricing::PriceSet, :price)
copy_nested_to_column(Taxes::VatRateSet, :vat_rate, :code, :vat_rate_code)
end
그 두 줄은 나에게 매우 우아하지만:
copy(Inventory::StockLevelChanged, :stock_level)
copy(Pricing::PriceSet, :price)
(우연히) 이벤트 속성을 열 이름과 일치시키는 규칙을 따르기 때문에 멋져 보입니다.
다른 라인은 여전히 약간의 사랑이 필요합니다.
첫째, 여기에서 레코드를 생성하려면 이름이 필요하며 빈 생성자가 아닙니다. 이 코드의 일반화를 찾기가 어려웠습니다.
마지막 줄은 엣지 케이스였습니다. 이벤트에 데이터가 중첩되어 있는 경우가 있습니다. 또한 열 이름과 일치하지 않습니다.
copy_nested_to_column(Taxes::VatRateSet, :vat_rate, :code, :vat_rate_code)
다음은 모두 작동하도록 지원하는 나머지 코드입니다.
private
def copy(event, attribute)
@cqrs.subscribe(-> (event) { copy_event_attribute_to_column(event, attribute, attribute) }, [event])
end
def copy_nested_to_column(event, top_event_attribute, nested_attribute, column)
@cqrs.subscribe(
-> (event) { copy_nested_event_attribute_to_column(event, top_event_attribute, nested_attribute, column) }, [event])
end
def register_product(event)
Product.create(id: event.data.fetch(:product_id), name: event.data.fetch(:name))
end
def copy_event_attribute_to_column(event, event_attribute, column)
product(event).update_attribute(column, event.data.fetch(event_attribute))
end
def copy_nested_event_attribute_to_column(event, top_event_attribute, nested_attribute, column)
product(event).update_attribute(column, event.data.fetch(top_event_attribute).fetch(nested_attribute))
end
def product(event)
find(event.data.fetch(:product_id))
end
def find(product_id)
Product.where(id: product_id).first
end
이 코드는 여전히 더 일반적이어야 합니다.
ActiveRecord 클래스 이름
Product
을 하드코딩 해제해야 합니다.그리고 당신을 상기시키기 위해. 나의 초기 목표는 새로운 읽기 모델을 만드는 것이었습니다. 여기서 제가 하는 일은 예비 리팩토링입니다. 그런 다음 몇 줄의 선언만으로 새로운 읽기 모델이 구현될 것으로 기대합니다.
우리는 그것이 어떻게되는지 볼 것입니다 :)
이러한 변경 사항이 포함된 커밋은 다음과 같습니다.
https://github.com/RailsEventStore/ecommerce/commit/9e42950fc7eb34257938b0501fa6e50733d0d568
읽어주셔서 감사합니다 ❤️
Reference
이 문제에 관하여(일반 읽기 모델 API 탐색), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/andrzejkrzywda/exploring-a-generic-read-model-api-56ck텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)