Vue.js, 기능 패턴별 클린 아키텍처 e 패키지

깨끗한 아키텍처를 새로운 방식으로 검토할 수 있습니다... Vue.js와 같은 방식으로 작업할 수 있습니다. Vue.js 시리즈의 Vamos 구현자 또는 프론티드 다 노사 API입니다.

바모스 라! nossa는 nossa api에 필요한 프런트엔드 개발자 os mesmos requisitos를 구현합니다.
  • Uma listagem de produtos
  • 목록에 있는 추가 제품에 대한 공식

  • 기능 패턴별 패키지



    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:
  • 소스
  • 모듈
  • 페이지 매김
  • 도메인
  • 모델


  • 상품
  • const
  • 부품
  • 저장소
  • 도메인
  • 모델
  • 사용 사례

  • 컨트롤러
  • 보기




  • 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 arquivosrc/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!



    좋은 웹페이지 즐겨찾기