Rustでクリーンアーキテクチャを組む時DIするサンプルコード
12481 ワード
はじめに
Rustでクリーンアーキテクチャを組もうとしていて、うまくレイヤーを分離できませんでした。
思想というよりは単にtraitやジェネリクスの正しい書き方を知らなかっただけでかなりの時間を要してしまったので、自分のメモを兼ねて書いてみようと思います。
構成としては
DB -> Repository -> Service
というようなレイヤーがあった時に、いかに分離(=DI)するかを想定しています。
関連型 + 関連関数での実装
#[derive(Debug)]
struct User {
age: u8
}
struct PgDatabase;
// 関連型を用いたトレイト
trait Repository {
// 関連型
type CreateRequest;
type CreateResponse;
// 関連関数
fn create(request:Self::CreateRequest) -> Self::CreateResponse;
}
impl Repository for PgDatabase {
// add code here
type CreateRequest = u8;
type CreateResponse = User;
fn create(age: u8) -> User {
User { age }
}
}
struct Service<U:Repository>(U);
impl <U:Repository> Service<U>{
pub fn new_with_age(&self, age: U::CreateRequest) -> U::CreateResponse {
U::create(age)
}
}
fn main() {
let db: PgDatabase = PgDatabase;
let service = Service::<PgDatabase>(db);
let user = service.new_with_age(20);
dbg!(user);
}
ジェネリクスとメソッドでの実装
#[derive(Debug, Clone, Copy)]
struct User {
id: u16,
age: u8
}
struct PgDatabase{
// DB機能のモックです
next_user_id: u16,
users: Vec<User>
}
// generic trait
trait Repository<T,U,V> {
fn create(&mut self, new_params:T) -> U;
fn find_by_id(&self, id: V) -> Option<&U>;
}
impl Repository<u8, User, u16> for PgDatabase {
// add code here
fn create(&mut self, age: u8) -> User {
let u = User{ age, id: self.next_user_id };
self.next_user_id = self.next_user_id + 1;
self.users.push(u);
u
}
fn find_by_id(&self, id: u16) -> Option<&User> {
self.users.iter().find(|user| { user.id == id })
}
}
struct Service<U:Repository<u8, User, u16>>{
repository: U
}
impl <U:Repository<u8, User, u16>> Service<U>{
pub fn new_with_age(&mut self, age: u8) -> User {
self.repository.create(age)
}
}
fn main() {
let db: PgDatabase = PgDatabase { next_user_id: 0, users: Vec::new()};
let mut service = Service::<PgDatabase>{repository: db};
let user = service.new_with_age(20);
dbg!(user);
}
#[derive(Debug)]
struct User {
age: u8
}
struct PgDatabase;
// 関連型を用いたトレイト
trait Repository {
// 関連型
type CreateRequest;
type CreateResponse;
// 関連関数
fn create(request:Self::CreateRequest) -> Self::CreateResponse;
}
impl Repository for PgDatabase {
// add code here
type CreateRequest = u8;
type CreateResponse = User;
fn create(age: u8) -> User {
User { age }
}
}
struct Service<U:Repository>(U);
impl <U:Repository> Service<U>{
pub fn new_with_age(&self, age: U::CreateRequest) -> U::CreateResponse {
U::create(age)
}
}
fn main() {
let db: PgDatabase = PgDatabase;
let service = Service::<PgDatabase>(db);
let user = service.new_with_age(20);
dbg!(user);
}
#[derive(Debug, Clone, Copy)]
struct User {
id: u16,
age: u8
}
struct PgDatabase{
// DB機能のモックです
next_user_id: u16,
users: Vec<User>
}
// generic trait
trait Repository<T,U,V> {
fn create(&mut self, new_params:T) -> U;
fn find_by_id(&self, id: V) -> Option<&U>;
}
impl Repository<u8, User, u16> for PgDatabase {
// add code here
fn create(&mut self, age: u8) -> User {
let u = User{ age, id: self.next_user_id };
self.next_user_id = self.next_user_id + 1;
self.users.push(u);
u
}
fn find_by_id(&self, id: u16) -> Option<&User> {
self.users.iter().find(|user| { user.id == id })
}
}
struct Service<U:Repository<u8, User, u16>>{
repository: U
}
impl <U:Repository<u8, User, u16>> Service<U>{
pub fn new_with_age(&mut self, age: u8) -> User {
self.repository.create(age)
}
}
fn main() {
let db: PgDatabase = PgDatabase { next_user_id: 0, users: Vec::new()};
let mut service = Service::<PgDatabase>{repository: db};
let user = service.new_with_age(20);
dbg!(user);
}
........その他はパターンを見つけ次第追記予定
終わりに
今の所2つのサンプルしかありませんが、他の実装パターンも追記予定です。
良くない書き方があるかと思いますので、お気づきの際にはご指摘いただければ幸いです。
Author And Source
この問題について(Rustでクリーンアーキテクチャを組む時DIするサンプルコード), 我々は、より多くの情報をここで見つけました https://qiita.com/kawadumax/items/0875eda8a89f5d24d3c3著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .