Phần phụ trợ cho Python là gì?

Vì vậy, bạn muốn tạo một ứng dụng web đầy đủ tính năng và bạn đang tự hỏi liệu mình nên sử dụng một framework lớn như Django hay thứ gì đó tối giản hơn như Flask?

Bài viết này mô tả cách chúng tôi xây dựng loại dịch vụ phụ trợ này theo các nguyên tắc sau

  • Kiến trúc dễ bảo trì
  • Sẵn sàng cho sản xuất
  • Tận dụng tối đa asyncio cho API, logic nghiệp vụ và tương tác với các hệ thống của bên thứ ba

Điều kiện tiên quyết . đã cài đặt Python gần đây [≥ 3. 9] với pip là đủ

1. Thiết kế hướng tên miền

Đầu tiên, hãy nói về kiến ​​trúc

Có rất nhiều điều để học hỏi từ thiết kế kiến ​​trúc khi bạn muốn xây dựng các ứng dụng trong thế giới thực. Khi bạn xem hầu hết các ví dụ mã do Flask hoặc FastAPI cung cấp, bạn sẽ có một ứng dụng rất đơn giản với API REST chỉ với một trình xử lý đơn giản cho mỗi điểm cuối. Trong các ứng dụng thực tế, bạn muốn tách logic nghiệp vụ của mình khỏi lệnh gọi API để có thể tương tác với ứng dụng thông qua các kênh khác như API GraphQL hoặc tin nhắn RabbitMQ. Bạn cũng cần xử lý một hoặc nhiều hệ thống lưu trữ, cơ sở dữ liệu, lớp bộ đệm, dịch vụ lưu trữ đối tượng, kho lưu trữ bí mật và các hệ thống phức tạp hơn như API của nhà cung cấp đám mây, Kubernetes, v.v.

Để thực hiện đúng cách tách các mối quan tâm và tương tác trừu tượng với các hệ thống khác, các khái niệm Thiết kế hướng miền [DDD] cung cấp một hộp công cụ tuyệt vời để xem xét. Sách Các mẫu kiến ​​trúc với Python [có sẵn trực tuyến tại đây ], là một mỏ vàng để hiểu cách triển khai kiến ​​trúc DDD trong Python. Nó cung cấp rất nhiều ví dụ từng bước cho mọi khái niệm để bạn có thể hiểu tại sao bạn nên hoặc không nên áp dụng chúng. Đây là cuốn sách phải đọc và hầu hết những gì được trình bày ở đây đều dựa trên cuốn sách này.

Vì vậy, chúng tôi sẽ hướng dẫn bạn một kiến ​​trúc bao gồm 3 lớp. Tên miền, ứng dụng và cơ sở hạ tầng. Lớp Miền xác định cấu trúc Dữ liệu trong các đối tượng Python đơn giản. các đối tượng kinh doanh. Lớp Ứng dụng nắm giữ bộ não của Ứng dụng. logic kinh doanh. Cuối cùng, lớp Cơ sở hạ tầng là "tay chân" của Ứng dụng của chúng tôi. phần tương tác với thế giới bên ngoài [API HTTP, cơ sở dữ liệu, hệ thống tệp, động cơ servo, v.v.]

Vì vậy, hãy tạo khung ứng dụng của chúng ta với 

├── myapp
│   ├── application
│   ├── domain
│   └── infrastructure
└── pyproject.toml
1 tuyệt vời

mkdir myapp
cd myapp
pip install poetry
poetry init
mkdir -p myapp/application
mkdir myapp/domain
mkdir myapp/infrastructure

Bạn nên có một cái gì đó như

├── myapp
│   ├── application
│   ├── domain
│   └── infrastructure
└── pyproject.toml

1. 1 miền

Lớp miền là một đại diện mô hình của các dịch vụ. Nó thực sự là cốt lõi của các dịch vụ của chúng tôi và nó phải có khả năng phát triển nhanh chóng. Lớp này không phụ thuộc vào bất kỳ lớp nào khác [tuân theo nguyên tắc đảo ngược phụ thuộc] và không nhập các thư viện bên ngoài [trừ khi có các ngoại lệ hợp lý, nó chỉ bao gồm mã python thô]

Miền là lớp dữ liệu xác định đối tượng nghiệp vụ. Hầu hết các phương thức của các lớp dữ liệu này bao gồm các trình trợ giúp thao túng trạng thái của lớp dữ liệu. Một số lớp này là lớp trừu tượng, được triển khai bởi các lớp khác từ lớp cơ sở hạ tầng.

Các phương thức của các lớp này có thể trả về các đối tượng Miền, trạng thái ["đã xảy ra lỗi", "không có vấn đề gì ở đây", "chỉ có bước 1 và 3 hoạt động"…] hoặc không có gì

Nguyên tắc chung là đặt càng nhiều thứ càng tốt ở đó

Ví dụ: đây là một đối tượng đại diện cho một mục trong ứng dụng việc cần làm của chúng tôi. Và vâng, ví dụ của chúng ta sẽ là một ứng dụng việc cần làm. [như tất cả chúng ta ^^]

import uuid
from datetime import datetime
from dataclasses import dataclass, field


@dataclass
class TodoEntry:
    id: str
    created_at: datetime
    content: str
    tags: set[str] = field[default_factory=set]

    @classmethod
    def create_from_dict[cls, content:str] -> "TodoEntry":
        return cls[id=str[uuid.uuid4[]], created_at=datetime.utcnow[], content=content]

    def set_tag[self, tag: str] -> None:
        self.tags.add[tag]

Bạn có nhận thấy rằng chúng tôi sử dụng nhiều loại Python không? . Chúng tôi thực sự khuyên bạn nên sử dụng chúng và thực thi nó trong CI để bạn không gặp bất ngờ khi thực hiện

1. 2 Cơ sở hạ tầng

Để quản lý tất cả các tương tác với các hệ thống bên ngoài như cơ sở dữ liệu, hệ thống tệp, mạng, API, v.v.

Các dịch vụ này đóng vai trò là "trình bao bọc" xung quanh các phụ thuộc bên ngoài để chúng có thể được sử dụng trong lớp Ứng dụng

1. 2. 1 Mẫu kho lưu trữ

Đây cũng là nơi chúng ta có thể tìm thấy Kho lưu trữ. Mẫu kho lưu trữ chỉ đơn giản là một lớp trừu tượng hóa tính bền vững của đối tượng. Nó cung cấp ít nhất 

├── myapp
│   ├── application
│   ├── domain
│   └── infrastructure
└── pyproject.toml
2 và 
├── myapp
│   ├── application
│   ├── domain
│   └── infrastructure
└── pyproject.toml
3 chức năng, cung cấp một cách duy nhất để lưu trữ và truy xuất dữ liệu từ hệ thống lưu trữ. Chúng tôi có thể bắt đầu với bộ lưu trữ tệp Pickle cho đến khi chúng tôi đạt đến giới hạn hiệu suất cho biết chúng tôi chuyển sang cơ sở dữ liệu SQL hoặc thứ gì đó khác. Quá trình này giúp chúng tôi không phải thay đổi bất kỳ dòng mã nào trong lớp Ứng dụng hoặc Miền của chúng tôi

Ví dụ: đây là kho lưu trữ 'Các mục cần làm' sử dụng thư viện Pickle để tuần tự hóa các đối tượng thành các tệp

import pickle
from dataclasses import dataclass
from pathlib import Path

from myapp.domain.todo import TodoEntry
from myapp.domain.todo_entry_repository import ITodoEntryRepository


class TodoEntryNotFound[Exception]:
    pass


@dataclass
class TodoEntryPickleRepository[ITodoEntryRepository]:
    storage_dir: str

    def get[self, entry_id: str] -> TodoEntry:
        try:
            entry: TodoEntry
            with open[Path[self.storage_dir] / entry_id] as entry_file:
                entry = pickle.load[entry_file]
            return entry
        except Exception:
            raise TodoEntryNotFound[]

    def add[self, entry: TodoEntry] -> None:
        with open[Path[self.storage_dir] / entry.id] as entry_file:
            pickle.dump[entry, entry_file

Lưu ý rằng chúng tôi triển khai một lớp trừu tượng trong lớp Miền. Điều này cho phép chúng tôi nhập giao diện kho lưu trữ từ lớp Ứng dụng mà không cần biết triển khai thực tế là gì

1. 3 ứng dụng

Bây giờ chúng ta có Miền chứa đối tượng nghiệp vụ cũng như Kho lưu trữ của chúng ta để quản lý tính bền vững của đối tượng này trong lớp Cơ sở hạ tầng, chúng ta cần gắn chúng lại với nhau bằng logic nghiệp vụ của mình
Lớp Ứng dụng chứa tất cả các dịch vụ do ứng dụng cung cấp, sử dụng cấu trúc Miền và Cơ sở hạ tầng làm phụ trợ

Các dịch vụ Ứng dụng này "sắp xếp" các cấu trúc của Miền và các dịch vụ Cơ sở hạ tầng để chúng hoạt động hài hòa với nhau

Dữ liệu ứng dụng không được sửa đổi ở đây; . Như đã đề cập trước đây, không có dữ liệu nào được sửa đổi trực tiếp ở đây. Tuy nhiên, chúng tôi nắm bắt các ngoại lệ và sử dụng các phương thức đối tượng để áp dụng đúng quy tắc kinh doanh

Ví dụ: chúng ta có thể có một TodoService như thế này

from dataclasses import dataclass
from typing import Optional

from myapp.domain.todo import TodoEntry
from myapp.domain.todo_entry_repository import ITodoEntryRepository


@dataclass
class TodoService:
    todo_repository: ITodoEntryRepository

    def add_entry[self, content: str] -> str:
        entry = TodoEntry.create_from_content[content]
        self.todo_repository.add[entry]
        return entry.id

    def add_tag[self, entry_id: str, tag: str] -> None:
        entry = self.todo_repository.get[entry_id]
        entry.set_tag[tag]

    def get_all[self, search: Optional[str] = None] -> list[TodoEntry]:
        return self.todo_repository.get_all[search]

Chờ đợi. todo_repository này được tạo ra khi nào và bởi ai?

Muốn tăng tốc
phát triển phụ trợ?

Bắt đầu chương trình phụ trợ của bạn

1. 4 Tiêm phụ thuộc

Mục tiêu của việc tiêm phụ thuộc là để tránh tạo các đối tượng ở mọi nơi hoặc chuyển chúng vào tất cả các chức năng trong một số loại 

├── myapp
│   ├── application
│   ├── domain
│   └── infrastructure
└── pyproject.toml
4 nồi nấu chảy. Để làm như vậy, chúng tôi sẽ xác định nơi tất cả các dịch vụ Cơ sở hạ tầng được tạo, ở một nơi duy nhất. Sau đó, chúng tôi có thể dễ dàng đưa các dịch vụ này làm phần phụ thuộc của các dịch vụ Ứng dụng bằng cách sử dụng giá trị mặc định là một đơn vị [e. g. cho kết nối cơ sở dữ liệu] hoặc đối tượng dùng một lần từ nhà máy [e. g. cho trình xử lý yêu cầu HTTP]

Thư viện  Dép phụ thuộc  được thiết kế tốt và cung cấp mọi thứ bạn cần để xác định tất cả dịch vụ của mình, đưa chúng vào và thậm chí tải cấu hình.

Hãy cài đặt nó

poetry add dependency_injector

Giải thích cấu hình và sự phụ thuộc trong vùng chứa. mã py

from dependency_injector import providers, containers

from myapp.application.todo_service import TodoService
from myapp.infrastructure.database.todo_entry_repository import TodoEntryPickleRepository


class ApplicationContainer[containers.DeclarativeContainer]:
    configuration = providers.Configuration[]

    todo_entry_repository = providers.Singleton[
        TodoEntryPickleRepository,
        storage_dir=configuration.storage_dir
    ]

    todo_service = providers.Factory[
        TodoService,
        todo_entry_repository
    ]

2. API web

Bây giờ chúng ta đã có ứng dụng cơ bản, chúng ta cần tạo một API. FastAPI  là một thư viện thực sự tuyệt vời giúp bạn tạo các điểm cuối API của mình, định tuyến chúng, tuần tự hóa và giải tuần tự hóa các đối tượng API [được gọi là mô hình] và thậm chí tạo tương tác .

Hãy thêm nó vào phần phụ thuộc của chúng tôi với

poetry add fastapi

Một cách thích hợp để thêm API của bạn là tách chúng theo bộ điều khiển, từng bộ theo nhóm điểm cuối. Vì vậy, hãy tạo một tệp thiết lập tập trung để tổng hợp cấu hình chung và nội dung phụ thuộc cho tất cả các bộ điều khiển trong 

├── myapp
│   ├── application
│   ├── domain
│   └── infrastructure
└── pyproject.toml
5

from fastapi import FastAPI

from myapp.container import ApplicationContainer
from myapp.infrastructure.api import todo_controller


def setup[app: FastAPI, container: ApplicationContainer] -> None:

    # Add other controllers here
    app.include_router[todo_controller.router]

    # Inject dependencies
    container.wire[
        modules=[
            todo_controller,
        ]
    ]

Và bộ điều khiển cho 

├── myapp
│   ├── application
│   ├── domain
│   └── infrastructure
└── pyproject.toml
6 điểm cuối

from dataclasses import asdict
from typing import Optional

from dependency_injector.wiring import Provide
from fastapi import APIRouter

from myapp.application.todo_service import TodoService
from myapp.container import ApplicationContainer
from myapp.infrastructure.api.todo_schema import TodoEntrySchema

todo_service: TodoService = Provide[ApplicationContainer.todo_service]

router = APIRouter[
    prefix="/todo",
    tags=["Todo"],
    responses={404: {"description": "Not found"}},
]


@router.get["/", response_model=list[TodoEntrySchema]]
async def list_todos[search: Optional[str] = None] -> list[TodoEntrySchema]:
    todo_entries = todo_service.get_all[search]
    return [TodoEntrySchema[**asdict[todo_entry]] for todo_entry in todo_entries]

@router.post["/"]
async def add_todo[content: str] -> str:
    return todo_service.add_entry[content]

Đây là lược đồ việc cần làm được sử dụng để tuần tự hóa. Lưu ý rằng chúng tôi sử dụng một đối tượng khác với 

├── myapp
│   ├── application
│   ├── domain
│   └── infrastructure
└── pyproject.toml
7 nội bộ từ miền vì chúng tôi muốn giải mã nó khỏi API bên ngoài. Do đó, bạn có thể thay đổi cách diễn đạt API của mình và ẩn các giá trị bên trong không hữu ích cho người dùng. Lược đồ dựa trên mô hình Pydantic sử dụng kiểu gõ tích hợp sẵn của Python. Như được quảng cáo trong tài liệu FastAPI, nó có rất nhiều ưu điểm như phân tích tĩnh với Mypy, tự động hoàn thành IDE hữu ích, gỡ lỗi dễ dàng, v.v.

├── myapp
│   ├── application
│   ├── domain
│   └── infrastructure
└── pyproject.toml
0

3. kiểm tra nó

Lưu ý rằng để đơn giản, chúng tôi đã giữ phần thử nghiệm cho đến nay. xấu hổ về chúng tôi. Đây là một trong những lý do chính khiến chúng tôi phân chia tất cả mã của mình theo cách này. Hãy nhớ rằng tất cả các thành phần có thể được kiểm tra dễ dàng riêng biệt hoặc trong ngữ cảnh. Tôi sẽ cho bạn một ví dụ. API của chúng tôi gọi dịch vụ ứng dụng, sau đó gọi kho lưu trữ và sau đó trả về danh sách việc cần làm được chuyển đổi từ đối tượng miền

Đây là một thử nghiệm đơn giản cho kho lưu trữ của chúng tôi trong 

├── myapp
│   ├── application
│   ├── domain
│   └── infrastructure
└── pyproject.toml
8

...

>> Trang tiếp theo >>

Trang tiếp theo

Michael Mercier

Kỹ sư phần mềm hàng đầu tại Ryax Technologies. Michael là Tiến sĩ Khoa học Máy tính và kỹ sư R&D, với kiến ​​thức chuyên môn rộng về cơ sở hạ tầng CNTT trong nhiều bối cảnh. Đám mây, Điện toán hiệu năng cao và Dữ liệu lớn

Ai sử dụng Python cho phụ trợ?

Python được sử dụng bởi Intel, IBM, NASA, Pixar, Netflix, Facebook, JP Morgan Chase, Spotify và một số công ty lớn khác. It's one of the four main languages at Google, while Google's YouTube is largely written in Python. Same with Reddit, Pinterest, and Instagram.

Mặt trước và mặt sau của Python là gì?

Các khía cạnh trực quan của trang web mà người dùng có thể nhìn thấy và trải nghiệm là giao diện người dùng. Mặt khác, mọi thứ xảy ra trong nền có thể được quy cho phần phụ trợ. Ngôn ngữ được sử dụng cho giao diện người dùng là HTML, CSS và JavaScript trong khi các ngôn ngữ được sử dụng cho giao diện người dùng sau bao gồm Java, Ruby, Python và. Net.

Chủ Đề