부르다:
cargo run
저장소 루트에서 갈 준비가되었습니다.
또한 API 문서를 탐색하여 모든 것이 어디에 살고 있는지에 대한 아이디어를 얻으십시오.
cargo doc --open
이 저장소에는 온라인 상점의 샘플 Rust 응용 프로그램이 포함되어 있습니다. 목표는 Rust Language를 활용하여 확장 가능하고 유지 관리 가능한 응용 프로그램을 구축하는 몇 가지 설계 패턴을 탐색하는 것입니다.
그것은 다른 아이디어를위한 놀이터이며, 그들 중 일부는 실제로 팬이되지 않을 수 있습니다. 여기에 무엇이든 피드백이 있으시면 언제든지 문제를 해결하십시오!
진공 상태에서 소프트웨어를 디자인하는 것은 어렵습니다. 중요한 것을 추진할 실제 도메인이 없으면 설계 결정은 임의적 인 느낌을 줄 수 있습니다. 나는 결정과 그에 대한 이유를 문서화하기 위해 노력했지만 주문에서 주문 항목을 분할 해야하는 것과 같은 질문은? 아니면 주문의 쿼리가 제품의 데이터베이스 테이블에 액세스 할 수 있어야합니까? 순전히 기술적 인 관점에서 실제로 대답 할 수는 없습니다. 그들은 프로젝트의 목표에 대한 관점이 필요합니다. 이 코드를 읽는 사람이라면 누구나 자의적 디자인 결정에 따라이를 면밀히 조사하는 것이 좋습니다. 자신의 환경에서 직면 한 제약과 녹슬로 응용 프로그램을 구축 할 때 자신의 결정을 알리는 방법을 생각해보십시오.
특정 녹 프레임 워크 또는 도서관에 관한 것이 아닙니다. 또는 온라인 쇼핑 응용 프로그램에 내재 된 문제 해결에 관한 것이 아닙니다.
다음 섹션에서는 응용 프로그램의 일부를 설명하고 그들이 왜 그 방식대로 정리하는지 설명합니다.
프로젝트 레이아웃은 개인 정보 보호에 중점을 둡니다. 특정 항목의 범위를 제한함으로써 잠재적 파손 범위를 제한합니다. 특정 항목의 범위를 제한함으로써 응용 프로그램 상태 유지 관리 부담의 범위도 제한됩니다. 녹에서는 모듈에 비공개 품목이 해당 모듈의 모든 어린이가 볼 수 있습니다 . 그것은 나쁜 것처럼 들릴지 모르지만 우리는 도메인 API가 직렬화 및 스토리지와 같은 외부 문제를 위해 구현 세부 사항을 유출하는 것을 방지하기 위해 활용합니다.
응용 프로그램의 각 핵심 비즈니스 개념은 products 이나 customers 같은 자체 자체 포함 폴더로 나뉩니다. 각 모듈은 특정 엔티티 세트에 대해 알아야 할 모든 것을 캡슐화합니다.
/store )/queries )/commands ) 엔티티는 제품을 추가 할 때 Product 에 따라 Order 같은 다른 모듈의 엔티티에 의존 할 수 있습니다. 각 도메인 모듈에는 개인 정보 보호 계층이 있습니다.
from_data 방법이 있습니다.from_data 에 수화 된 엔티티에 따라 다릅니다.이 모듈은 약간 무겁지만 적절한 응용 프로그램에서는 매크로를 사용하여 새로운 도메인 모듈을 추가하는 것을 단순화 할 수 있습니다. 이 애플리케이션에서 매크로를 사용하지 않았으므로 코드를 쉽게 따라갈 수 있습니다.
완벽하게 제작 된 모듈 계층의 한 가지 문제는 단순히 현재 레이아웃에 맞지 않는 개념으로 끝날 때 모두 떨어질 수 있다는 것입니다. 더 자주 발생할수록, 그것이 무엇인지 말하기가 불가능하기 때문에 이전에 존재했던 레이아웃을 준수하기가 더 어려워집니다.
우리는이 모듈이 자신의 운명을 관리하기를 원하지만, 별도의 서비스로 나눌 수있는 시점에 자체 포함되기를 원하지 않습니다. 이것은 물건을 단순하게 유지하는 것입니다. 이 작업을 수행하고 싶다면 별도의 모듈 대신 별도의 상자를 사용하는 것이 좋습니다.
응용 프로그램은 간단한 명령 쿼리 책임 분리 설계를 따릅니다. 이것은 복잡한 논리가없는 데이터 중심 애플리케이션에 잘 작동하는 접근법입니다. 명령은 일부 도메인 상호 작용을 캡처하고 엔티티에서 직접 작업하는 반면 쿼리는 완전히 임의적입니다. 이 응용 프로그램은 CQR을 실현하기 위해 특별한 인프라를 사용하지 않으며 종속성 주입 패턴을 사용하여 구현 된 간단한 특성 만 사용합니다. 본질적으로 :
Result<()>Result<T> 를 반환합니다&mut self 수신기가 필요합니다&self 수신기가 필요합니다돌연변이의 차이는 명령이 쿼리를 호출 할 수 있지만 쿼리는 명령을 호출 할 수 없다는 것을 의미합니다.
엔티티는 응용 프로그램의 핵심입니다. 실제 사업이 부족 했음에도 불구하고 도메인 모델을 풍부하게 유지하기 위해 노력했습니다. 엔티티는 단지 Cruddy State의 가방이 아닙니다. 그들은 다음과 같습니다.
.to_data() 호출하여 엔티티의 읽기 전용 보기를 얻을 수 있습니다. 엔티티를 보는 동안 동작 수정을 호출 할 수 없습니다. 이것은 Rust의 차용 시스템에 의해 보장됩니다. 엔티티는 .into_data() 사용하여 소유권을 읽기 전용 데이터로 옮길 수 있습니다. 이것은 단방향 작업이므로 상태로의 변경 사항을 상점으로 다시 유지할 수 없습니다.엔티티의 목표는 일부 주요 도메인 개념의 불변량을 캡슐화하는 것입니다. 여기의 엔티티는 모의 메모리 내 상점 또는 외부 데이터베이스와 함께 사용하기 쉽습니다. 우리는 한 엔티티가 같은 소스를 가리키기 때문에 한 엔티티에 반영된 상태 변경에 의존하지 않도록주의해야합니다.
엔티티는 또한 데이터가 실제로 유효하다는 보장이 없기 때문에 다른 엔티티의 데이터 유형에 의존하지 않도록주의해야합니다. 대신 그들은 엔티티에 의존하여 필요에 따라 데이터로 변환하므로 상태가 유효하다는 것을 항상 알고 있습니다.
우리는 다음과 같은 녹 기능을 사용하여 엔티티 상태를 보호합니다.
Serialize 또는 Deserialize 구현하지 않습니다. 이것은 트랙 아래로 변경 될 수 있지만, 순간화 가능한 상태를 거꾸로 호환되도록 빠르게 유지하는 것이 더 쉽다는 것을 알았습니다.엔티티는 일부 상태 또는 데이터를 캡슐화하고 해당 데이터에 대한 변경이 데이터가 보유 할 것으로 예상되는 불변량을 중단하지 않도록합니다. Getters를 구현하는 대신 구조로서 데이터의 읽기 전용보기를 노출시킵니다. 장점은 Getter 방법과 마찬가지로 데이터 구조 작업을위한 Rust의 멋진 기능을 포기할 필요가 없다는 것입니다. 이보기는 읽기 전용 이므로 변경 사항은 구조로 직접 다시 작성할 수 없습니다. 엔티티는 여전히 세터 방법을 제공합니다.
이러한 방식으로 상태를 노출하면 공개 가치가없는 version 과 같이 구현 세부 사항이 누출된다고 주장 할 수 있습니다. 이것은 아마도 사실 일 것입니다. 주변에서 작업하기 위해 읽기 전용 뷰의 수명을 필드로 옮기고 잠재적으로 다른 차용 상태를 구성하고 데이터 구조를 개인이 관리하는 데 데이터 구조를 유지할 수 있습니다.
또한 저장하지 않는 구조에 불변량을 잡는 것은 부서지기 쉬운 것이라고 주장 할 수 있습니다. 이것은 일부 필드의 개인 정보 보호 경계가 C#과 같이 객체 수준에있을 때 의미가 있습니다. 녹은 약간 다릅니다. 가장 큰 개인 정보 보호 경계는 모듈과 어린이에 있습니다. 따라서 주어진 필드의 불변량을 유지하는 부담은 정의 된 모듈의 모든 항목과 해당 모듈의 모든 어린이에 해당됩니다.
이것은 끔찍한 유출처럼 들릴 수 있지만이 응용 프로그램은이를 활용하여 멋지게 추상화 된 스토리지를 구축합니다. ORM을 지원하기 위해 API에 구멍을 노출시키는 대신, 불변의 상태를 유지하는 것은 단순히 대중에게 유출되지 않고 단순히 모델 저장소로 확장됩니다.
Id 및 Version 유형에는 모두 Phantom Generic 매개 변수가 있습니다. 이 매개 변수는 순전히 Id<ProductData> 및 Id<OrderData> 와 같은 호환 유형의 ID를 표현할 수 있지만 여전히 다른 구현 세부 사항을 공유 할 수 있습니다.
매크로를 사용하여 보일러 플레이트를 줄이기보다 쉽게 따라갈 수있는 패턴입니다.
각 지속 가능한 엔티티에는 version 필드가 있습니다. 이 필드는 주어진 시점에서 엔티티의 상태에 해당하는 비 순차적 식별자입니다. 매장에서 엔티티를 가져 오면 버전을 수화시킨 다음 업데이트 직전에 확인하고 일치하지 않으면 우리는 발행합니다.
버전 검사는 데이터에 대한 독점 잠금 장치가 있기 때문에 메모리 내 매장에 잘 작동하지만 (한 번에 한 번에 상태를 수정할 수 있음) 적절한 DB에는 다른 접근 방식이 필요합니다. ID와 버전이 일치하는 곳에서 업데이트하고 업데이트 된 레코드 수를 선택하고 0 인 경우 Balk를 업데이트 할 수 있습니다 (버전이 일치하지 않았거나 존재하지 않음).
스토리지 계층은 독립 데이터 저장소가 트랜잭션에 참여할 수있는 간단한 트랜잭션 체계를 사용합니다. 중앙 저장소는 활성 트랜잭션을 추적하고 데이터 스토어에서 데이터를 가져 오면 사용할 준비가되도록 상담합니다. 데이터의 낙관적 동시성은 여러 활성 트랜잭션에서 동시에 동일한 값을 설정할 수 없도록합니다. 이것은 진정한 고립을 위반하지만, 사물을 단순하게 유지하고 저장되는 각 값에 필요한 상태를 최소화 할 수 있습니다.
의존성 주입은 응용 프로그램을 설계 할 때 의지하는 관행으로 유리합니다. 의존성 해상도의 문제를 앱 로직과 분리 할 수 있습니다. 또한 응용 프로그램을 확장하는 명백한 방법을 제공합니다. 이 응용 프로그램은 많은 인프라없이 이러한 이점을 제공하는 간단한 패턴을 채택합니다.
이 응용 프로그램은 .NET 애플리케이션을 작성하는 경우에 익숙 할 수있는 것처럼 제어 컨테이너의 역전을 사용하지 않습니다. 이것은 실제로 녹에 대한 것이 없기 때문입니다. 어려운 문제입니다. 정교한 컨테이너가 없어도 명령과 쿼리를 작성하기 위해 간단한 의존성 주입 패턴을 사용합니다.
여기서 의존성 주입의 주요 목표는 조롱을 지원하는 것이 아닙니다. 말초 문제를 개별 구성 요소의 논리로부터 더 멀리 밀어서 복잡성을 줄이는 것입니다.
주사 가능한 구성 요소는 자체 모듈에 살고 있습니다. 해당 모듈에는 다음이 포함됩니다.
Resolver 유형의 임플란트 블록.impl Trait 반환합니다. 이 기본 구현에서 어떤 콘크리트 유형이 사용하는지 알 수 없습니다.Arc , Box 와 같은 몇 가지 스마트 포인터에 대해 구현 된 Blanket의 구성 요소를 설명하는 특성. 공유 Resolver 약간의 서비스-로운데 들리지만, 의존성 해상도는 분야 자체의 임플란트 블록에 전적으로 포함되어 있기 때문에 앱 Resolver 에서 Magic Global State에 따라 문제를 피합니다.
보일러 플레이트를 줄이기 위해 단일 방법 만있는 구성 요소의 경우 Fn 특성을 위해이를 구현합니다. 이를 통해 모든 종속성에 대한 일반적인 구조를 선언하지 않아도됩니다. 녹 컴파일러가 당신을 위해 그것을 처리합니다.
이 패턴은 산문으로 설명하기가 어렵습니다. domain/products/commands/create_product 모듈 또는 작업 중이 종속성 주입 패턴의 예를 보려면 domain/products/model/store 모듈을 살펴보십시오.
Resolver "God Object"가 아닌가? God Object는 " 신청에서 모든 중요한 논리를 신청서를 수집하는 모든 중요한 논리를 God Object를 통해 작업 할 수 없다는 점으로 모든 중요한 논리를 수집하는 모든 중요한 논리를 수집하는 것입니다. 그들은 구성하거나 변화하기가 어려워지기 때문에 문제가됩니다. 여기서 Resolver 패턴은 신 대상이지만 개별 구성 요소를 구성 할 필요는 없습니다. Resolver 구성 요소를 건설 할 필요가 없습니다.