Vue.js, 기능 패턴별 클린 아키텍처 e 패키지
43280 단어 vuecleanarchitecturejavascriptwebdev
바모스 라! nossa는 nossa api에 필요한 프런트엔드 개발자 os mesmos requisitos를 구현합니다.
기능 패턴별 패키지
Nesta estrutura de projeto, os pacotes contêm todas as class necessárias para um recurso. A independência do pacote é assegurada colocando class intimamente relacionadas no mesmo pacote. Aqui um post com um ótimo exemplo de como funciona.
구현
Primeira coisa que precisamos fazer é criar nosso projeto vue, ainda com o Vue 2 com typescript.
vue create clean-vue
cd clean-vue
vue add vuetify
npm i axios
npm i @types/axios --save-dev
npm run serve
Projetinho rodando liso, bora codar!
Vamos apagar a 파스타
src/components
e estruturar o projeto da seguinte forma:Agora tudo está dando erro, nada mais funciona! Calma lá, vamos estruturar nosso código que tudo se resolution. :디
모델
Primeira coisa é nós definirmos o model com o que volta da API no arquivo
src/module/product/domain/model/product.ts
.import { AxiosResponse } from "axios"
interface ProductI {
id?: number
name?: string
description?: string
price?: number
}
class Product {
id: number
name: string
description: string
price: number
constructor({ id = 0, name = "", description = "", price = 0.00 }: ProductI) {
this.id = id
this.name = name
this.description = description
this.price = price
}
}
class ProductPagination {
items: ProductI[]
total: number
constructor(response?: AxiosResponse) {
this.items = response?.data?.items?.map((product: any) => new Product(product)) ?? []
this.total = response?.data?.total ?? 0
}
}
export { Product, ProductPagination }
페이지 모델은 기본적으로 문서가 없는 응용 프로그램
src/module/pagination/domain/model/pagination.ts
에 있습니다.interface PaginationI {
page: number
itemsPerPage: number
sort: string
descending: string
search: string
}
class Pagination {
page: number
itemsPerPage: number
sort: string
descending: string
search: string
constructor({ page, itemsPerPage, sort, descending, search }: PaginationI) {
this.page = page
this.itemsPerPage = itemsPerPage
this.descending = descending
this.search = search
this.sort = sort
}
}
export { Pagination }
저장소
com nossos 모델은 prontos, podemos는 nosso 저장소를 준비하고 os 끝점 dos nossos produtos를 조작합니다.
Criaremos o arquivo
src/module/product/repository/fetchProductsRepository.ts
.import { Pagination } from '@/module/pagination/domain/model/pagination'
import { ProductPagination } from '../domain/model/product'
import { AxiosInstance } from 'axios'
interface FetchProductsRepository {
(pagination: Pagination): Promise<ProductPagination>
}
const fetchProductsRepository = (axios: AxiosInstance): FetchProductsRepository => async (pagination: Pagination) => {
const response = await axios.get("/product", {
params: pagination
})
const productPagination = new ProductPagination(response)
return productPagination
}
export { fetchProductsRepository, FetchProductsRepository }
E também criaremos o arquivo
src/module/product/repository/createProductRepository.ts
.import { Product } from '../domain/model/product'
import { AxiosInstance } from 'axios'
interface CreateProductRepository {
(product: Product): Promise<Product>
}
const createProductRepository = (axios: AxiosInstance): CreateProductRepository => async (product: Product) => {
const response = await axios.post("/product", product)
return new Product(response?.data)
}
export { createProductRepository, CreateProductRepository }
유스케이스
com nossos repositories criados, podemos implementar nosso usecase de produtos.
Criaremos o arquivo
src/module/product/domain/usecase/fetchProductsUseCase.ts
.import { FetchProductsRepository } from "../../repository/fetchProductsRepository"
import { Pagination } from "@/module/pagination/domain/model/pagination"
import { ProductPagination } from "../model/product"
import { DataOptions } from "vuetify"
interface FetchProductsUseCase {
(options: DataOptions, search: string): Promise<ProductPagination>
}
const fetchProductsUseCase = (repository: FetchProductsRepository): FetchProductsUseCase => async (options: DataOptions, search: string) => {
const pagination = new Pagination({
descending: options.sortDesc.join(","),
sort: options.sortBy.join(","),
page: options.page,
itemsPerPage: options.itemsPerPage,
search: search,
})
const productPagination = await repository(pagination)
return productPagination
}
export { fetchProductsUseCase, FetchProductsUseCase }
E também criaremos o arquivo
src/module/product/domain/usecase/createProductUseCase.ts
.import { CreateProductRepository } from "../../repository/createProductRepository"
import { Product } from "../model/product"
interface CreateProductsUseCase {
(product: Product): Promise<Product>
}
const createProductUseCase = (repository: CreateProductRepository): CreateProductsUseCase => async (product: Product) => {
const productCreated = await repository(product)
return productCreated
}
export { createProductUseCase, CreateProductsUseCase }
제어 장치
Com nossos usecases criados, podemos implementar nosso Controller no arquivomodule/product/controller/productController.ts
.
import { CreateProductsUseCase } from "../domain/usecase/createProductUseCase";
import { FetchProductsUseCase } from "../domain/usecase/fetchProductUseCase";
import { Product, ProductPagination } from "../domain/model/product";
import { headers } from "../const/header";
class ProductController {
options: any
public product = new Product({})
public productPagination = new ProductPagination()
public headers = headers
public formDialog = false
constructor(
private context: any,
private fetchProductsUseCase: FetchProductsUseCase,
private createProductUseCase: CreateProductsUseCase
) { }
async paginate() {
this.productPagination = await this.fetchProductsUseCase(this.options, "")
}
async save() {
if (this.context.$refs.productForm.$refs.form.validate()) {
await this.createProductUseCase(this.product)
this.cancel()
this.paginate()
}
}
cancel() {
this.product = new Product({})
this.context.$refs.productForm.$refs.form.resetValidation()
this.formDialog = false
}
}
export { ProductController }
서둘러! Brincadeira... Estamos quase lá, vamos configurar nossa injeção de dependsências. Para configurar a injeção de dependsência do nosso product vamos criar um arquivo emmodule/di/di.ts
.
import { fetchProductsRepository } from "../product/repository/fetchProductsRepository";
import { createProductRepository } from "../product/repository/createProductRepository";
import { createProductUseCase } from "../product/domain/usecase/createProductUseCase";
import { fetchProductsUseCase } from "../product/domain/usecase/fetchProductUseCase";
import { ProductController } from "../product/controller/productController";
import axios from "axios";
const axiosInstance = axios.create({
baseURL: "https://clean-go.herokuapp.com",
headers: {
"Content-Type": "application/json"
}
})
axiosInstance.interceptors.response.use((response) => response, async (err) => {
const status = err.response ? err.response.status : null
if (status === 500) {
// Do something here or on any status code return
}
return Promise.reject(err);
});
// Implementation methods from products feature
const fetchProductsRepositoryImpl = fetchProductsRepository(axiosInstance)
const fetchProductsUseCaseImpl = fetchProductsUseCase(fetchProductsRepositoryImpl)
const createProductRepositoryImpl = createProductRepository(axiosInstance)
const createProductUseCaseImpl = createProductUseCase(createProductRepositoryImpl)
const productController = (context: any) => new ProductController(
context,
fetchProductsUseCaseImpl,
createProductUseCaseImpl
)
export { productController }
Agora sim, bora para nossa tela! Fique a vontade para montar ela do jeito que quiser!
Criaremos o arquivo module/product/components/productTable.vue
<template>
<v-card>
<v-card-title>
Products
<v-spacer></v-spacer>
<v-btn
color="primary"
@click="controller.formDialog = true"
>
<v-icon left>mdi-plus</v-icon>new
</v-btn>
</v-card-title>
<v-card-text class="pa-0">
<v-data-table
dense
:items="controller.productPagination.items"
:headers="controller.headers"
:options.sync="controller.options"
@pagination="controller.paginate()"
:server-items-length="controller.productPagination.total"
></v-data-table>
</v-card-text>
</v-card>
</template>
<script>
export default {
props: {
controller: {
require: true,
},
},
};
</script>
에오 아르키보module/product/components/productForm.vue
<template>
<v-dialog
persistent
width="400"
v-model="controller.formDialog"
>
<v-card>
<v-card-title class="pa-0 pb-4">
<v-toolbar
flat
dense
color="primary"
class="white--text"
>
New product
</v-toolbar>
</v-card-title>
<v-card-text>
<v-form ref="form">
<v-text-field
label="Name"
dense
filled
v-model="controller.product.name"
:rules="[(v) => !!v || 'Required']"
></v-text-field>
<v-text-field
label="Price"
dense
filled
v-model.number="controller.product.price"
:rules="[(v) => !!v || 'Required']"
></v-text-field>
<v-textarea
label="Description"
dense
filled
v-model="controller.product.description"
:rules="[(v) => !!v || 'Required']"
></v-textarea>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn
@click="controller.cancel()"
color="red"
text
>cancel</v-btn>
<v-spacer></v-spacer>
<v-btn
color="primary"
@click="controller.save()"
>
<v-icon left>mdi-content-save</v-icon>save
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
props: {
controller: {
require: true,
},
},
};
</script>
E por fim criaremos o arquivo module/product/view/product.vue
<template>
<v-app>
<v-main>
<v-row
class="fill-height"
justify="center"
align="center"
>
<v-col
cols="12"
lg="6"
>
<product-table
ref="productTable"
:controller="controller"
/>
<product-form
ref="productForm"
:controller="controller"
/>
</v-col>
</v-row>
</v-main>
</v-app>
</template>
<script>
import { productController } from "../../di/di";
import ProductTable from "../components/productTable";
import ProductForm from "../components/productForm";
export default {
components: {
ProductTable,
ProductForm,
},
data: (context) => ({
controller: productController(context),
}),
};
</script>
E a estrutura final ficou:
Testando, 1..2..3.. Teste som!
Reference
이 문제에 관하여(Vue.js, 기능 패턴별 클린 아키텍처 e 패키지), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/booscaaa/vuejs-clean-architecture-e-package-by-feature-pattern-56lb
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
import { CreateProductsUseCase } from "../domain/usecase/createProductUseCase";
import { FetchProductsUseCase } from "../domain/usecase/fetchProductUseCase";
import { Product, ProductPagination } from "../domain/model/product";
import { headers } from "../const/header";
class ProductController {
options: any
public product = new Product({})
public productPagination = new ProductPagination()
public headers = headers
public formDialog = false
constructor(
private context: any,
private fetchProductsUseCase: FetchProductsUseCase,
private createProductUseCase: CreateProductsUseCase
) { }
async paginate() {
this.productPagination = await this.fetchProductsUseCase(this.options, "")
}
async save() {
if (this.context.$refs.productForm.$refs.form.validate()) {
await this.createProductUseCase(this.product)
this.cancel()
this.paginate()
}
}
cancel() {
this.product = new Product({})
this.context.$refs.productForm.$refs.form.resetValidation()
this.formDialog = false
}
}
export { ProductController }
import { fetchProductsRepository } from "../product/repository/fetchProductsRepository";
import { createProductRepository } from "../product/repository/createProductRepository";
import { createProductUseCase } from "../product/domain/usecase/createProductUseCase";
import { fetchProductsUseCase } from "../product/domain/usecase/fetchProductUseCase";
import { ProductController } from "../product/controller/productController";
import axios from "axios";
const axiosInstance = axios.create({
baseURL: "https://clean-go.herokuapp.com",
headers: {
"Content-Type": "application/json"
}
})
axiosInstance.interceptors.response.use((response) => response, async (err) => {
const status = err.response ? err.response.status : null
if (status === 500) {
// Do something here or on any status code return
}
return Promise.reject(err);
});
// Implementation methods from products feature
const fetchProductsRepositoryImpl = fetchProductsRepository(axiosInstance)
const fetchProductsUseCaseImpl = fetchProductsUseCase(fetchProductsRepositoryImpl)
const createProductRepositoryImpl = createProductRepository(axiosInstance)
const createProductUseCaseImpl = createProductUseCase(createProductRepositoryImpl)
const productController = (context: any) => new ProductController(
context,
fetchProductsUseCaseImpl,
createProductUseCaseImpl
)
export { productController }
<template>
<v-card>
<v-card-title>
Products
<v-spacer></v-spacer>
<v-btn
color="primary"
@click="controller.formDialog = true"
>
<v-icon left>mdi-plus</v-icon>new
</v-btn>
</v-card-title>
<v-card-text class="pa-0">
<v-data-table
dense
:items="controller.productPagination.items"
:headers="controller.headers"
:options.sync="controller.options"
@pagination="controller.paginate()"
:server-items-length="controller.productPagination.total"
></v-data-table>
</v-card-text>
</v-card>
</template>
<script>
export default {
props: {
controller: {
require: true,
},
},
};
</script>
<template>
<v-dialog
persistent
width="400"
v-model="controller.formDialog"
>
<v-card>
<v-card-title class="pa-0 pb-4">
<v-toolbar
flat
dense
color="primary"
class="white--text"
>
New product
</v-toolbar>
</v-card-title>
<v-card-text>
<v-form ref="form">
<v-text-field
label="Name"
dense
filled
v-model="controller.product.name"
:rules="[(v) => !!v || 'Required']"
></v-text-field>
<v-text-field
label="Price"
dense
filled
v-model.number="controller.product.price"
:rules="[(v) => !!v || 'Required']"
></v-text-field>
<v-textarea
label="Description"
dense
filled
v-model="controller.product.description"
:rules="[(v) => !!v || 'Required']"
></v-textarea>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn
@click="controller.cancel()"
color="red"
text
>cancel</v-btn>
<v-spacer></v-spacer>
<v-btn
color="primary"
@click="controller.save()"
>
<v-icon left>mdi-content-save</v-icon>save
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
props: {
controller: {
require: true,
},
},
};
</script>
<template>
<v-app>
<v-main>
<v-row
class="fill-height"
justify="center"
align="center"
>
<v-col
cols="12"
lg="6"
>
<product-table
ref="productTable"
:controller="controller"
/>
<product-form
ref="productForm"
:controller="controller"
/>
</v-col>
</v-row>
</v-main>
</v-app>
</template>
<script>
import { productController } from "../../di/di";
import ProductTable from "../components/productTable";
import ProductForm from "../components/productForm";
export default {
components: {
ProductTable,
ProductForm,
},
data: (context) => ({
controller: productController(context),
}),
};
</script>
Reference
이 문제에 관하여(Vue.js, 기능 패턴별 클린 아키텍처 e 패키지), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/booscaaa/vuejs-clean-architecture-e-package-by-feature-pattern-56lb텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)