おそらく、Flama 1.7 の最近のリリースについてはすでに聞いたことがあるでしょう。これは、ML API の開発と製品化に役立ついくつかのエキサイティングな新機能をもたらしました。この投稿は、そのリリースの主なハイライトの 1 つである ドメイン駆動設計のサポート に特化しています。ただし、実際の例で詳細を説明する前に、次のリソースに留意することをお勧めします (まだ理解していない場合は、よく理解しておくこと)。
それでは、新機能の使用を開始し、それを活用して堅牢で保守可能な ML API を構築する方法を見てみましょう。
この投稿は次のように構成されています:
現代のソフトウェア開発では、ビジネス ロジックをアプリケーションの技術設計と調整することが不可欠です。ここでドメイン駆動設計 (DDD) が威力を発揮します。 DDD は、ビジネスの中核領域を反映するソフトウェアの構築に重点を置き、ビジネス概念に基づいてコードを整理することで複雑な問題を解決します。これにより、DDD は開発者が保守可能でスケーラブルで堅牢なアプリケーションを作成できるようにします。以下では、知っておくべき DDD の最も重要な概念を私たちが考えるものを紹介します。それらについて詳しく説明する前に、この投稿は DDD の包括的なガイドを目的としたものではなく、またこのトピックに関する主要な参考文献の代替を目的としたものではないことを述べておきます。実際、DDD をより深く理解するには、次のリソースをお勧めします:
DDD の重要な概念をさらに詳しく説明する前に、これらがアプリのコンテキストで示され、どのように相互接続されているかを示す、Cosmic Python による非常に役立つ図を参照することをお勧めします。 .
ドメイン モデル の概念は、その用語の単純な定義によって説明できます。
したがって、ドメイン モデルは、ビジネスの仕組みについてビジネスオーナーが頭の中に持つ一連の概念とルールを参照するための派手な (しかし標準的で便利な) 方法です。これは、一般に、アプリケーションのビジネス ロジックとも呼ばれるもので、システムの動作を制御するルール、制約、関係が含まれます。
今後、ドメイン モデルをモデルと呼びます。
リポジトリ パターンは、モデルをデータ アクセスから切り離すことを可能にする設計パターンです。リポジトリ パターンの背後にある主なアイデアは、データ アクセス ロジックとアプリケーションのビジネス ロジックの間に抽象化レイヤーを作成することです。この抽象化レイヤーにより関心事の分離が可能になり、コードの保守性とテスト性が向上します。
リポジトリ パターンを実装するときは、通常、他のリポジトリが実装する必要がある標準メソッド (AbstractRepository) を指定するインターフェイスを定義します。そして、データ アクセス ロジックが実装されるこれらのメソッドの具体的な実装を使用して、特定のリポジトリが定義されます (SQLAlchemyRepository など)。この設計パターンは、データ操作メソッドを分離して、アプリケーション内の他の場所でシームレスに使用できるようにすることを目的としています。私たちのドメインモデルで。
作業単位パターン は、モデルをデータ アクセスから最終的に切り離すために欠落している部分です。作業単位はデータ アクセス ロジックをカプセル化し、単一のトランザクション内でデータ ソースに対して実行する必要があるすべての操作をグループ化する方法を提供します。このパターンでは、すべての操作がアトミックに実行されることが保証されます。
作業単位パターンを実装するときは、通常、他の作業単位が実装する必要がある標準メソッド (AbstractUnitOfWork) を指定するインターフェイスを定義します。そして、データ アクセス ロジックが実装されるこれらのメソッドの具体的な実装を使用して、特定の作業単位が定義されます (SQLAlchemyUnitOfWork など)。この設計により、アプリケーションのビジネス ロジックの実装を変更することなく、データ ソースへの接続を体系的に処理できるようになります。
DDD の主な概念を簡単に紹介した後、Flama を使用した DDD の実装に入る準備が整いました。このセクションでは、開発環境のセットアップ、ベース アプリケーションの構築、Flama を使用した DDD コンセプトの実装のプロセスを説明します。
例に進む前に、今確認した主要な DDD 概念に関する Flama の命名規則を確認してください。
上の図からわかるように、命名規則は非常に直感的です。リポジトリはリポジトリ パターンを指します。そして、Worker は作業単位を指します。ここで、DDD を使用する Flama API の実装に進むことができます。ただし、始める前に、flama を使用して単純な API を作成する方法、またはコードの準備ができた後に API を実行する方法についての基本を確認する必要がある場合は、次のことを確認してください。クイックスタートガイドを出してください。ここには、この投稿を進めるために必要な基本的な概念と手順が記載されています。それでは、早速、実装を始めましょう。
最初のステップは、開発環境を作成し、このプロジェクトに必要な依存関係をすべてインストールすることです。良い点は、この例では、flama をインストールするだけで、JWT 認証を実装するために必要なツールがすべて揃っていることです。依存関係を管理するために詩を使用しますが、必要に応じて pip を使用することもできます:
poetry add "flama[full]" "aiosqlite"
aiosqlite パッケージは、この例で使用するデータベースである SQLAlchemy で SQLite を使用するために必要です。
私たちが通常どのようにプロジェクトを編成しているかを知りたい場合は、こちらの前回の投稿をご覧ください。そこでは、詩を使用した Python プロジェクトのセットアップ方法と、通常従うプロジェクト フォルダー構造について詳しく説明しています。
単一のパブリック エンドポイントを持つ単純なアプリケーションから始めましょう。このエンドポイントは、API の簡単な説明を返します。
# src/app.py from flama import Flama app = Flama( title="Domain-driven API", version="1.0.0", description="Domain-driven design with Flama ?", docs="/docs/", ) @app.get("/", name="info") def info(): """ tags: - Info summary: Ping description: Returns a brief description of the API responses: 200: description: Successful ping. """ return {"title": app.schema.title, "description": app.schema.description, "public": True}
このアプリケーションを実行したい場合は、上記のコードを src フォルダーの下の app.py というファイルに保存し、次のコマンドを実行します (詩環境をアクティブ化することを忘れないでください。アクティブ化していない場合は、コマンドの前に詩 run を付けます):
flama run --server-reload src.app:app INFO: Started server process [3267] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
--server-reload フラグはオプションであり、コードが変更されたときにサーバーを自動的にリロードするために使用されます。これは開発中に非常に便利ですが、必要ない場合は削除できます。利用可能なオプションの完全なリストについては、flama run --help を実行するか、ドキュメントを確認してください。
また、次のスクリプトを実行してアプリケーションを実行することもできます。このスクリプトは、src フォルダーの下に __main__.py として保存できます。
# src/__main__.py import flama def main(): flama.run( flama_app="src.app:app", server_host="0.0.0.0", server_port=8000, server_reload=True ) if __name__ == "__main__": main()
次に、次のコマンドを実行してアプリケーションを実行できます:
poetry add "flama[full]" "aiosqlite"
アプリケーションの最小限のスケルトンを設定したので、
内で今確認した DDD 概念の実装を開始できます。
現実世界のシナリオを模倣しようとする簡単な例のコンテキスト。ユーザーを管理する API の開発を要求され、次の要件が与えられたとします。
この一連の要件は、これまでアプリケーションの ドメイン モデル と呼ばれていたものを構成します。これは本質的には、次のユーザー ワークフローを具体化したものに他なりません。
次に、リポジトリとワーカー パターンを使用してドメイン モデルを実装しましょう。まずデータ モデルを定義し、次にリポジトリとワーカー パターンを実装します。
ユーザー データは SQLite データベースに保存されます (SQLAlchemy でサポートされている他のデータベースを使用できます)。次のデータ モデルを使用してユーザーを表します (このコードは、src フォルダーの下の models.py というファイルに保存できます):
# src/app.py from flama import Flama app = Flama( title="Domain-driven API", version="1.0.0", description="Domain-driven design with Flama ?", docs="/docs/", ) @app.get("/", name="info") def info(): """ tags: - Info summary: Ping description: Returns a brief description of the API responses: 200: description: Successful ping. """ return {"title": app.schema.title, "description": app.schema.description, "public": True}
データ モデルの他に、データベースとテーブルを作成するための移行スクリプトが必要です。このために、プロジェクトのルートにある migrations.py というファイルに次のコードを保存できます。
poetry add "flama[full]" "aiosqlite"
次に、次のコマンドを実行して移行スクリプトを実行できます。
# src/app.py from flama import Flama app = Flama( title="Domain-driven API", version="1.0.0", description="Domain-driven design with Flama ?", docs="/docs/", ) @app.get("/", name="info") def info(): """ tags: - Info summary: Ping description: Returns a brief description of the API responses: 200: description: Successful ping. """ return {"title": app.schema.title, "description": app.schema.description, "public": True}
この例では、必要なリポジトリは 1 つだけです。つまり、ユーザー テーブルでのアトミック操作を処理するリポジトリであり、名前は UserRepository になります。ありがたいことに、flama は SQLAlchemyTableRepository という SQLAlchemy テーブルに関連するリポジトリの基本クラスを提供します。
クラス SQLAlchemyTableRepository は、テーブルに対して CRUD 操作を実行する一連のメソッドを提供します。具体的には次のとおりです。
この例では、テーブルに対してこれ以上のアクションは必要ないため、SQLAlchemyTableRepository によって提供されるメソッドで十分です。次のコードを src フォルダーの下の repositories.py というファイルに保存できます。
flama run --server-reload src.app:app INFO: Started server process [3267] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
ご覧のとおり、UserRepository クラスは SQLAlchemyTableRepository のサブクラスであり、テーブルを _table 属性に設定することのみが必要です。ユーザー テーブルの完全に機能するリポジトリを作成するために必要なのはこれだけです。
標準の CRUD 操作以外にカスタム メソッドを追加したい場合は、UserRepository クラスで定義することで追加できます。たとえば、アクティブ ユーザーの数をカウントするメソッドを追加したい場合は、次のように実行できます:
# src/__main__.py import flama def main(): flama.run( flama_app="src.app:app", server_host="0.0.0.0", server_port=8000, server_reload=True ) if __name__ == "__main__": main()
この例ではこのメソッドは使用しませんが、必要に応じてカスタム メソッドをリポジトリに追加できることと、その実装方法を知っておくとよいでしょう。
リポジトリ パターンのコンテキストで。すでにおわかりのとおり、これは強力な設計パターンです。アプリケーションのビジネス ロジック (対応するリソース メソッドに実装されている) を変更することなく、すべてのデータ アクセス ロジックをここに実装できるためです。
作業単位パターンは、データ アクセス ロジックをカプセル化し、単一のトランザクション内でデータ ソースに対して実行する必要があるすべての操作をグループ化する方法を提供するために使用されます。 flama では、UoW パターンが Worker という名前で実装されます。リポジトリ パターンと同じように、flama は SQLAlchemyWorker と呼ばれる、SQLAlchemy テーブルに関連するワーカーの基本クラスを提供します。基本的に、SQLAlchemyWorker はデータベースへの接続とトランザクションを提供し、ワーカー接続を使用してすべてのリポジトリをインスタンス化します。この例では、ワーカーは 1 つのリポジトリ (つまり UserRepository) のみを使用しますが、必要に応じてさらにリポジトリを追加することもできます。
ワーカーは RegisterWorker という名前になり、次のコードを src フォルダーの下の works.py というファイルに保存できます。
poetry add "flama[full]" "aiosqlite"
したがって、ProductRepository や OrderRepository など、操作するリポジトリがさらにある場合は、次のようにワーカーに追加できます。
# src/app.py from flama import Flama app = Flama( title="Domain-driven API", version="1.0.0", description="Domain-driven design with Flama ?", docs="/docs/", ) @app.get("/", name="info") def info(): """ tags: - Info summary: Ping description: Returns a brief description of the API responses: 200: description: Successful ping. """ return {"title": app.schema.title, "description": app.schema.description, "public": True}
非常に簡単ですが、アプリケーションにリポジトリとワーカー パターンを実装しました。ここで、ユーザー データと対話するために必要な API エンドポイントを提供するリソース メソッドの実装に進むことができます。
リソースは、flama アプリケーションの主要な構成要素の 1 つです。これらは、アプリケーション リソース (RESTful リソースの意味で) を表し、それらと対話する API エンドポイントを定義するために使用されます。
この例では、UserResource というユーザーのリソースを定義します。これには、ユーザーを作成、アクティブ化、サインイン、および非アクティブ化するためのメソッドが含まれます。リソースは、少なくとも flama 組み込み Resource クラスから派生する必要がありますが、flama は、RESTResource や CRUDResource など、操作するためのより高度なクラスを提供します。
次のコードを src フォルダーの下の resource.py というファイルに保存できます。
flama run --server-reload src.app:app INFO: Started server process [3267] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
データ モデル、リポジトリとワーカー パターン、リソース メソッドを実装したので、すべてが期待どおりに動作するように、前に紹介したベース アプリケーションを変更する必要があります。次のことを行う必要があります:
これにより、app.py ファイルは次のようになります:
poetry add "flama[full]" "aiosqlite"
DDD パターンにより、アプリケーションの ビジネス ロジック (リソース メソッドで簡単に読み取れる) を データ アクセス ロジック からどのように分離できるかは、すでに明らかなはずです。 🎜> (リポジトリとワーカー パターンに実装されます)。また、この関心事の分離によってコードがどのように保守しやすく、テストしやすくなったのか、またコードがこの例の冒頭で与えられたビジネス要件とどのように一致するようになったのかにも注目する価値があります。
コマンドを実行する前に、開発環境が正しく設定されていること、およびフォルダー構造が次のとおりであることを確認してください。
# src/app.py from flama import Flama app = Flama( title="Domain-driven API", version="1.0.0", description="Domain-driven design with Flama ?", docs="/docs/", ) @app.get("/", name="info") def info(): """ tags: - Info summary: Ping description: Returns a brief description of the API responses: 200: description: Successful ping. """ return {"title": app.schema.title, "description": app.schema.description, "public": True}
すべてが正しく設定されている場合は、次のコマンドを実行してアプリケーションを実行できます (アプリケーションを実行する前に必ず移行スクリプトを実行してください)。
flama run --server-reload src.app:app INFO: Started server process [3267] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
これで、実装したばかりのビジネス ロジックを試すことができます。これを試すには、curl や Postman などのツールを使用するか、ブラウザで http://localhost:8000/docs/ に移動して flama が提供する自動生成ドキュメント UI を使用します。そこからエンドポイントを試します。
ユーザーを作成するには、次のペイロードを含む POST リクエストを /user/ に送信できます。
# src/__main__.py import flama def main(): flama.run( flama_app="src.app:app", server_host="0.0.0.0", server_port=8000, server_reload=True ) if __name__ == "__main__": main()
したがって、curl を使用して次のようにリクエストを送信できます。
poetry run python src/__main__.py INFO: Started server process [3267] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
リクエストが成功すると、空の本文を含む 200 レスポンスが返され、データベースにユーザーが作成されます。
サインインするには、次のペイロードを使用して POST リクエストを /user/signin/ に送信できます。
# src/models.py import uuid import sqlalchemy from flama.sqlalchemy import metadata from sqlalchemy.dialects.postgresql import UUID __all__ = ["user_table", "metadata"] user_table = sqlalchemy.Table( "user", metadata, sqlalchemy.Column("id", UUID(as_uuid=True), primary_key=True, nullable=False, default=uuid.uuid4), sqlalchemy.Column("name", sqlalchemy.String, nullable=False), sqlalchemy.Column("surname", sqlalchemy.String, nullable=False), sqlalchemy.Column("email", sqlalchemy.String, nullable=False, unique=True), sqlalchemy.Column("password", sqlalchemy.String, nullable=False), sqlalchemy.Column("active", sqlalchemy.Boolean, nullable=False), )
したがって、curl を使用して次のようにリクエストを送信できます。
# migrations.py from sqlalchemy import create_engine from src.models import metadata if __name__ == "__main__": # Set up the SQLite database engine = create_engine("sqlite:///models.db", echo=False) # Create the database tables metadata.create_all(engine) # Print a success message print("Database and User table created successfully.")
ユーザーがアクティブではない場合、次のような応答を受け取るはずです:
> poetry run python migrations.py Database and User table created successfully.
誰かが間違ったパスワードでサインインしようとした場合に何が起こるかをテストすることもできます:
# src/repositories.py from flama.ddd import SQLAlchemyTableRepository from src import models __all__ = ["UserRepository"] class UserRepository(SQLAlchemyTableRepository): _table = models.user_table
この場合、次の本文を含む 401 応答を受信する必要があります:
# src/repositories.py from flama.ddd import SQLAlchemyTableRepository from src import models __all__ = ["UserRepository"] class UserRepository(SQLAlchemyTableRepository): _table = models.user_table async def count_active_users(self): return len((await self._connection.execute(self._table.select().where(self._table.c.active == True))).all())
最後に、存在しないユーザーでのサインインも試行する必要があります:
# src/workers.py from flama.ddd import SQLAlchemyWorker from src import repositories __all__ = ["RegisterWorker"] class RegisterWorker(SQLAlchemyWorker): user: repositories.UserRepository
この場合、次の本文を含む 404 応答を受信する必要があります:
# src/workers.py from flama.ddd import SQLAlchemyWorker from src import repositories __all__ = ["RegisterWorker"] class RegisterWorker(SQLAlchemyWorker): user: repositories.UserRepository product: repositories.ProductRepository order: repositories.OrderRepository
サインインプロセスを調べたので、ユーザーの資格情報を使用して POST リクエストを /user/activate/ に送信することでユーザーをアクティブ化できます。
# src/resources.py import hashlib import http import uuid from flama import types from flama.ddd.exceptions import NotFoundError from flama.exceptions import HTTPException from flama.http import APIResponse from flama.resources import Resource, resource_method from src import models, schemas, worker __all__ = ["AdminResource", "UserResource"] ENCRYPTION_SALT = uuid.uuid4().hex ENCRYPTION_PEPER = uuid.uuid4().hex class Password: def __init__(self, password: str): self._password = password def encrypt(self): return hashlib.sha512( (hashlib.sha512((self._password + ENCRYPTION_SALT).encode()).hexdigest() + ENCRYPTION_PEPER).encode() ).hexdigest() class UserResource(Resource): name = "user" verbose_name = "User" @resource_method("/", methods=["POST"], name="create") async def create(self, worker: worker.RegisterWorker, data: types.Schema[schemas.UserDetails]): """ tags: - User summary: User create description: Create a user responses: 200: description: User created in successfully. """ async with worker: try: await worker.user.retrieve(email=data["email"]) except NotFoundError: await worker.user.create({**data, "password": Password(data["password"]).encrypt(), "active": False}) return APIResponse(status_code=http.HTTPStatus.OK) @resource_method("/signin/", methods=["POST"], name="signin") async def signin(self, worker: worker.RegisterWorker, data: types.Schema[schemas.UserCredentials]): """ tags: - User summary: User sign in description: Create a user responses: 200: description: User signed in successfully. 401: description: User not active. 404: description: User not found. """ async with worker: password = Password(data["password"]) try: user = await worker.user.retrieve(email=data["email"]) except NotFoundError: raise HTTPException(status_code=http.HTTPStatus.NOT_FOUND) if user["password"] != password.encrypt(): raise HTTPException(status_code=http.HTTPStatus.UNAUTHORIZED) if not user["active"]: raise HTTPException( status_code=http.HTTPStatus.BAD_REQUEST, detail=f"User must be activated via /user/activate/" ) return APIResponse(status_code=http.HTTPStatus.OK, schema=types.Schema[schemas.User], content=user) @resource_method("/activate/", methods=["POST"], name="activate") async def activate(self, worker: worker.RegisterWorker, data: types.Schema[schemas.UserCredentials]): """ tags: - User summary: User activate description: Activate an existing user responses: 200: description: User activated successfully. 401: description: User activation failed due to invalid credentials. 404: description: User not found. """ async with worker: try: user = await worker.user.retrieve(email=data["email"]) except NotFoundError: raise HTTPException(status_code=http.HTTPStatus.NOT_FOUND) if user["password"] != Password(data["password"]).encrypt(): raise HTTPException(status_code=http.HTTPStatus.UNAUTHORIZED) if not user["active"]: await worker.user.update({**user, "active": True}, id=user["id"]) return APIResponse(status_code=http.HTTPStatus.OK) @resource_method("/deactivate/", methods=["POST"], name="deactivate") async def deactivate(self, worker: worker.RegisterWorker, data: types.Schema[schemas.UserCredentials]): """ tags: - User summary: User deactivate description: Deactivate an existing user responses: 200: description: User deactivated successfully. 401: description: User deactivation failed due to invalid credentials. 404: description: User not found. """ async with worker: try: user = await worker.user.retrieve(email=data["email"]) except NotFoundError: raise HTTPException(status_code=http.HTTPStatus.NOT_FOUND) if user["password"] != Password(data["password"]).encrypt(): raise HTTPException(status_code=http.HTTPStatus.UNAUTHORIZED) if user["active"]: await worker.user.update({**user, "active": False}, id=user["id"]) return APIResponse(status_code=http.HTTPStatus.OK)
このリクエストでは、ユーザーがアクティブ化され、空の本文を含む 200 レスポンスを受け取るはずです。
前のケースと同様に、誰かが間違ったパスワードでユーザーをアクティブ化しようとした場合に何が起こるかをテストすることもできます。
poetry add "flama[full]" "aiosqlite"
この場合、次の本文を含む 401 応答を受信する必要があります:
# src/app.py from flama import Flama app = Flama( title="Domain-driven API", version="1.0.0", description="Domain-driven design with Flama ?", docs="/docs/", ) @app.get("/", name="info") def info(): """ tags: - Info summary: Ping description: Returns a brief description of the API responses: 200: description: Successful ping. """ return {"title": app.schema.title, "description": app.schema.description, "public": True}
最後に、存在しないユーザーのアクティブ化も試行する必要があります:
flama run --server-reload src.app:app INFO: Started server process [3267] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
この場合、次の本文を含む 404 応答を受信する必要があります:
# src/__main__.py import flama def main(): flama.run( flama_app="src.app:app", server_host="0.0.0.0", server_port=8000, server_reload=True ) if __name__ == "__main__": main()
ユーザーがアクティブ化されたので、再度サインインを試みることができます:
poetry run python src/__main__.py INFO: Started server process [3267] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
今回は、ユーザーの情報を含む 200 応答が返されます。
# src/models.py import uuid import sqlalchemy from flama.sqlalchemy import metadata from sqlalchemy.dialects.postgresql import UUID __all__ = ["user_table", "metadata"] user_table = sqlalchemy.Table( "user", metadata, sqlalchemy.Column("id", UUID(as_uuid=True), primary_key=True, nullable=False, default=uuid.uuid4), sqlalchemy.Column("name", sqlalchemy.String, nullable=False), sqlalchemy.Column("surname", sqlalchemy.String, nullable=False), sqlalchemy.Column("email", sqlalchemy.String, nullable=False, unique=True), sqlalchemy.Column("password", sqlalchemy.String, nullable=False), sqlalchemy.Column("active", sqlalchemy.Boolean, nullable=False), )
最後に、ユーザーの資格情報を使用して POST リクエストを /user/deactivate/ に送信することで、ユーザーを非アクティブ化できます。
# migrations.py from sqlalchemy import create_engine from src.models import metadata if __name__ == "__main__": # Set up the SQLite database engine = create_engine("sqlite:///models.db", echo=False) # Create the database tables metadata.create_all(engine) # Print a success message print("Database and User table created successfully.")
このリクエストでは、ユーザーは非アクティブ化され、空の本文を含む 200 レスポンスを受け取るはずです。
この投稿では、ドメイン駆動設計 (DDD) の世界と、それを flama アプリケーションに実装する方法について説明しました。 DDD がアプリケーションのビジネス ロジックをデータ アクセス ロジックから分離するのにどのように役立つか、またこの懸念事項の分離によってコードがどのように保守しやすく、テストしやすくなるかを見てきました。また、リポジトリとワーカー パターンを flama アプリケーションに実装する方法と、それらを使用してデータ アクセス ロジックをカプセル化し、実行する必要があるすべての操作をグループ化する方法を提供する方法についても説明しました。単一トランザクション内のデータ ソース上で。最後に、リソース メソッドを使用してユーザー データと対話する API エンドポイントを定義する方法と、DDD パターンを使用してこの例の冒頭で示したビジネス要件を実装する方法を確認しました。
ここで説明したサインイン プロセスは完全に現実的ではありませんが、この記事の内容と JWT 認証に関する以前の投稿を組み合わせて、より現実的なプロセスを実装できます。このプロセスでは、サインインは最終的に を返します。 JWT トークン。これに興味がある場合は、flama による JWT 認証に関する投稿をチェックしてください。
この投稿が役に立ち、独自の flama アプリケーションに DDD を実装する準備ができたことを願っています。ご質問やご意見がございましたら、お気軽にお問い合わせください。いつでも喜んでお手伝いさせていただきます!
フラマや、AI とソフトウェア開発の世界におけるその他のエキサイティングなトピックに関するさらなる投稿にご期待ください。次回まで!
私たちの活動を気に入っていただけましたら、無料で簡単に私たちの活動をサポートする方法があります。 Flama で ⭐ をプレゼントしてください。
GitHub ⭐ は私たちにとって世界を意味し、堅牢な機械学習 API を構築する旅の他の人々を支援するために、GitHub に取り組み続けるための最もおいしい燃料を与えてくれます。
? で私たちをフォローすることもできます。そこでは、AI、ソフトウェア開発などに関する興味深いスレッドのほかに、最新のニュースや更新情報を共有しています。
以上がFlama を使用したネイティブ ドメイン駆動設計の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。