Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.
Strukturieren Sie ein Python-Projekt in hexagonaler Architektur mit Lambda AWS
Erstellt von Furkan Oruc (AWS), Dominik Goby (), Darius Kunce (AWS) und Michal Ploski () AWS AWS
Umgebung: PoC oder Pilotprojekt | Technologien: Web- und mobile Apps; Container und Mikroservices; Serverlos; Modernisierung | AWSDienste: Amazon DynamoDB; AWS Lambda; Amazon Gateway API |
Übersicht
Dieses Muster zeigt, wie ein Python-Projekt in hexagonaler Architektur mithilfe AWS von Lambda strukturiert wird. Das Muster verwendet das AWS Cloud Development Kit (AWSCDK) als Infrastructure as Code (IaC) -Tool, Amazon API Gateway als und Amazon DynamoDB als Persistenzschicht. REST API Die hexagonale Architektur folgt domänengesteuerten Entwurfsprinzipien. In der hexagonalen Architektur besteht Software aus drei Komponenten: Domäne, Ports und Adaptern. Ausführliche Informationen zu sechseckigen Architekturen und ihren Vorteilen finden Sie im Leitfaden Aufbau sechseckiger Architekturen auf. AWS
Voraussetzungen und Einschränkungen
Voraussetzungen
Ein aktives Konto AWS
Erfahrung in Python
Vertrautheit mit AWS Lambda AWSCDK, Amazon API Gateway und DynamoDB
Ein GitHub Konto (siehe Anweisungen zur Registrierung)
Git (siehe Installationsanleitung)
Ein Code-Editor, mit dem Sie Änderungen vornehmen und Ihren Code übertragen können GitHub (z. B. Visual Studio Code oder JetBrains PyCharm)
Docker ist installiert und der Docker-Daemon ist betriebsbereit
Produktversionen
Git Version 2.24.3 oder höher
Python-Version 3.7 oder höher
AWSCDKv2
Poesie-Version 1.1.13 oder höher
AWSLambda Powertools für Python Version 1.25.6 oder höher
pytest Version 7.1.1 oder höher
Moto-Version 3.1.9 oder höher
pydantic Version 1.9.0 oder höher
Boto3 Version 1.22.4 oder höher
mypy-boto3-dynamodb Version 1.24.0 oder höher
Architektur
Zieltechnologie-Stack
Der Zieltechnologie-Stack besteht aus einem Python-Dienst, der API Gateway, Lambda und DynamoDB verwendet. Der Dienst verwendet einen DynamoDB-Adapter, um Daten zu speichern. Es bietet eine Funktion, die Lambda als Einstiegspunkt verwendet. Der Service verwendet Amazon API Gateway, um a verfügbar zu machen RESTAPI. Der API verwendet AWS Identity and Access Management (IAM) für die Authentifizierung von Clients.
Zielarchitektur
Zur Veranschaulichung der Implementierung wird in diesem Muster eine serverlose Zielarchitektur bereitgestellt. Clients können Anfragen an einen API Gateway-Endpunkt senden. APIGateway leitet die Anfrage an die Lambda-Zielfunktion weiter, die das hexagonale Architekturmuster implementiert. Die Lambda-Funktion führt Erstellungs-, Lese-, Aktualisierungs- und Löschvorgänge (CRUD) in einer DynamoDB-Tabelle durch.
Wichtig: Dieses Muster wurde in einer PoC-Umgebung getestet. Bevor Sie eine Architektur in einer Produktionsumgebung einsetzen, müssen Sie eine Sicherheitsüberprüfung durchführen, um das Bedrohungsmodell zu identifizieren und eine sichere Codebasis zu erstellen. |
---|
Die API unterstützt fünf Operationen an einer Produktentität:
GET /products
gibt alle Produkte zurück.
POST /products
erstellt ein neues Produkt.
GET /products/{id}
gibt ein bestimmtes Produkt zurück.
PUT /products/{id}
aktualisiert ein bestimmtes Produkt.
DELETE /products/{id}
löscht ein bestimmtes Produkt.
Sie können die folgende Ordnerstruktur verwenden, um Ihr Projekt so zu organisieren, dass es dem hexagonalen Architekturmuster folgt:
app/ # application code
|--- adapters/ # implementation of the ports defined in the domain
|--- tests/ # adapter unit tests
|--- entrypoints/ # primary adapters, entry points
|--- api/ # api entry point
|--- model/ # api model
|--- tests/ # end to end api tests
|--- domain/ # domain to implement business logic using hexagonal architecture
|--- command_handlers/ # handlers used to execute commands on the domain
|--- commands/ # commands on the domain
|--- events/ # events triggered via the domain
|--- exceptions/ # exceptions defined on the domain
|--- model/ # domain model
|--- ports/ # abstractions used for external communication
|--- tests/ # domain tests
|--- libraries/ # List of 3rd party libraries used by the Lambda function
infra/ # infrastructure code
simple-crud-app.py # AWS CDK v2 app
AWSDienste
Amazon API Gateway ist ein vollständig verwalteter Service, der Entwicklern die Erstellung, Veröffentlichung, Wartung, Überwachung und Sicherung APIs in jeder Größenordnung erleichtert.
Amazon DynamoDB ist eine vollständig verwaltete, serverlose SQL Key-Value-No-Datenbank, die für die Ausführung von Hochleistungsanwendungen in jeder Größenordnung konzipiert ist.
AWSLambda ist ein serverloser, ereignisgesteuerter Rechendienst, mit dem Sie Code für praktisch jede Art von Anwendung oder Backend-Service ausführen können, ohne Server bereitstellen oder verwalten zu müssen. Sie können Lambda-Funktionen von über 200 AWS Diensten und SaaS-Anwendungen (Software as a Service) aus starten und zahlen nur für das, was Sie tatsächlich nutzen.
Tools
Git wird in diesem Muster als Versionskontrollsystem für die Codeentwicklung verwendet.
Python wird als Programmiersprache für dieses Muster verwendet. Python bietet Datenstrukturen auf hoher Ebene und einen Ansatz für objektorientierte Programmierung. AWSLambda bietet eine integrierte Python-Laufzeit, die den Betrieb von Python-Diensten vereinfacht.
Visual Studio Code wird IDE für die Entwicklung und das Testen dieses Musters verwendet. Sie können jedes verwendenIDE, das die Python-Entwicklung unterstützt (z. B. PyCharm).
AWSCloud Development Kit (AWSCDK) ist ein Open-Source-Framework für die Softwareentwicklung, mit dem Sie Ihre Cloud-Anwendungsressourcen mithilfe vertrauter Programmiersprachen definieren können. Dieses Muster verwendet dieCDK, um die Cloud-Infrastruktur als Code zu schreiben und bereitzustellen.
Poesie wird verwendet, um Abhängigkeiten im Muster zu verwalten.
Docker wird von der verwendet, AWS CDK um das Lambda-Paket und die Lambda-Layer zu erstellen.
Code
Der Code für dieses Muster ist im Beispiel-Repository für die hexagonale Architektur von GitHub Lambda verfügbar.
Bewährte Methoden
Um dieses Muster in einer Produktionsumgebung zu verwenden, folgen Sie diesen bewährten Methoden:
Verwenden Sie vom Kunden verwaltete Schlüssel in AWS Key Management Service (AWSKMS), um CloudWatch Amazon-Protokollgruppen und Amazon DynamoDB-Tabellen zu verschlüsseln.
Konfigurieren Sie AWSWAFAmazon API Gateway so, dass der Zugriff nur über das Netzwerk Ihres Unternehmens möglich ist.
Ziehen Sie andere Optionen für die API Gateway-Autorisierung in Betracht, wenn diese IAM nicht Ihren Anforderungen entspricht. Sie können beispielsweise Amazon Cognito Cognito-Benutzerpools oder APIGateway Lambda-Autorisierer verwenden.
Verwenden Sie DynamoDB-Backups.
Konfigurieren Sie Lambda-Funktionen mit einer Virtual Private Cloud (VPC) -Bereitstellung, um den Netzwerkverkehr in der Cloud zu halten.
Aktualisieren Sie die zulässige Ursprungskonfiguration für Cross-Origin Resource Sharing (CORS) Preflight, um den Zugriff nur auf die anfragende Ursprungsdomäne zu beschränken.
Verwenden Sie cdk-nag, um den AWS CDK Code auf bewährte Sicherheitsmethoden zu überprüfen.
Erwägen Sie die Verwendung von Tools zum Scannen von Code, um häufig auftretende Sicherheitsprobleme im Code zu finden. Bandit ist beispielsweise ein Tool, das entwickelt wurde, um häufig auftretende Sicherheitsprobleme im Python-Code zu finden. PIP-Audit durchsucht Python-Umgebungen nach Paketen, die bekannte Sicherheitslücken aufweisen.
Dieses Muster verwendet AWSX-Ray, um Anfragen über den Einstiegspunkt, die Domäne und die Adapter der Anwendung zu verfolgen. AWSX-Ray hilft Entwicklern dabei, Engpässe zu erkennen und hohe Latenzen zu ermitteln, um die Anwendungsleistung zu verbessern.
Epen
Aufgabe | Beschreibung | Erforderliche Fähigkeiten |
---|
Erstellen Sie Ihr eigenes Repository. | Loggen Sie sich ein bei GitHub. Erstellen Sie ein neues Repository. Anweisungen finden Sie in der GitHub Dokumentation. Klonen Sie das Beispiel-Repository für dieses Muster und übertragen Sie es in das neue Repository in Ihrem Konto.
| App-Developer |
Installieren Sie die Abhängigkeiten. | Installieren Sie Poetry. pip install poetry
Installiere Pakete aus dem Stammverzeichnis. Der folgende Befehl installiert die Anwendung und die AWS CDK Pakete. Außerdem werden Entwicklungspakete installiert, die für die Ausführung von Komponententests erforderlich sind. Alle installierten Pakete werden in einer neuen virtuellen Umgebung platziert. poetry install
Um eine grafische Darstellung der installierten Pakete zu sehen, führen Sie den folgenden Befehl aus. poetry show --tree
Aktualisieren Sie alle Abhängigkeiten. poetry update
Öffnen Sie eine neue Shell in der neu erstellten virtuellen Umgebung. Sie enthält alle installierten Abhängigkeiten. poetry shell
| App-Developer |
Konfiguriere deineIDE. | Wir empfehlen Visual Studio Code, aber Sie können jeden beliebigen Code IDE Ihrer Wahl verwenden, der Python unterstützt. Die folgenden Schritte beziehen sich auf Visual Studio Code. Aktualisieren Sie die .vscode/settings Datei. {
"python.testing.pytestArgs": [
"app/adapters/tests",
"app/entrypoints/api/tests",
"app/domain/tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"python.envFile": "${workspaceFolder}/.env",
}
Erstellen Sie eine .env Datei im Stammverzeichnis des Projekts. Dadurch wird sichergestellt, dass das Stammverzeichnis des Projekts im Verzeichnis enthalten ist, PYTHONPATH sodass Sie es pytest finden und alle Pakete richtig erkennen können. PYTHONPATH=.
| App-Developer |
Führen Sie Komponententests aus, Option 1: Verwenden Sie Visual Studio Code. | Wählen Sie den Python-Interpreter der virtuellen Umgebung, die von Poetry verwaltet wird. Führen Sie Tests im Test Explorer aus.
| App-Developer |
Unit-Tests ausführen, Option 2: Verwenden Sie Shell-Befehle. | Starten Sie eine neue Shell in der virtuellen Umgebung. poetry shell
Führen Sie den pytest Befehl im Stammverzeichnis aus. python -m pytest
Alternativ können Sie den Befehl direkt von Poetry aus ausführen. poetry run python -m pytest
| App-Developer |
Aufgabe | Beschreibung | Erforderliche Fähigkeiten |
---|
Fordern Sie temporäre Anmeldeinformationen an. | Um bei der Ausführung über AWS Anmeldeinformationen auf der Shell zu verfügencdk deploy , erstellen Sie mithilfe von AWS IAM Identity Center (Nachfolger von AWS Single Sign-On) temporäre Anmeldeinformationen. Anweisungen finden Sie im Blogbeitrag So rufen Sie kurzfristige Anmeldeinformationen für die CLI Verwendung mit AWS IAM Identity Center ab. | App-Entwickler, AWS DevOps |
Stellen Sie die Anwendung bereit. | Installieren Sie die AWS CDK Version 2. npm install -g aws-cdk
Weitere Informationen finden Sie in der AWSCDKDokumentation. Booten Sie das AWS CDK in Ihr Konto und Ihre Region. cdk bootstrap aws://12345678900/us-east-1 --profile aws-profile-name
Stellen Sie die Anwendung mithilfe eines AWS Profils als AWS CloudFormation Stack bereit. cdk deploy --profile aws-profile-name
| App-Entwickler, AWS DevOps |
Testen Sie dasAPI, Option 1: Verwenden Sie die Konsole. | Verwenden Sie die APIGateway-Konsole, um das zu testenAPI. Weitere Informationen zu API Vorgängen und Anforderungs-/Antwortnachrichten finden Sie im Abschnitt API Verwendung der Readme-Datei im Repository. GitHub | App-Entwickler, AWS DevOps |
Testen Sie dasAPI, Option 2: Verwenden Sie Postman. | Wenn Sie ein Tool wie Postman verwenden möchten: Installieren Sie Postman als eigenständige Anwendung oder Browsererweiterung. Kopieren Sie den Endpunkt URL für das API Gateway. Es wird das folgende Format haben. https://{api-id}.execute-api.{region}.amazonaws.com/{stage}/{path}
Konfigurieren Sie die AWS Signatur auf der Registerkarte Autorisierung. Anweisungen finden Sie im AWS re:POST-Artikel zur Aktivierung der IAM Authentifizierung für API Gateway. REST APIs Verwenden Sie Postman, um Anfragen an Ihren Endpunkt zu senden. API
| App-Entwickler, AWS DevOps |
Aufgabe | Beschreibung | Erforderliche Fähigkeiten |
---|
Schreiben Sie Komponententests für den Geschäftsbereich. | Erstellen Sie eine Python-Datei in dem app/domain/tests Ordner, indem Sie das test_ Dateinamenpräfix verwenden. Erstellen Sie eine neue Testmethode, um die neue Geschäftslogik anhand des folgenden Beispiels zu testen. def test_create_product_should_store_in_repository():
# Arrange
command = create_product_command.CreateProductCommand(
name="Test Product",
description="Test Description",
)
# Act
create_product_command_handler.handle_create_product_command(
command=command, unit_of_work=mock_unit_of_work
)
# Assert
Erstellen Sie eine Befehlsklasse im app/domain/commands Ordner. Wenn die Funktionalität neu ist, erstellen Sie im app/domain/command_handlers Ordner einen Stub für den Befehlshandler. Führen Sie den Komponententest aus, um festzustellen, ob er fehlschlägt, da es immer noch keine Geschäftslogik gibt. python -m pytest
| App-Developer |
Implementieren Sie Befehle und Befehlshandler. | Implementieren Sie Geschäftslogik in der neu erstellten Befehlshandler-Datei. Deklarieren Sie für jede Abhängigkeit, die mit externen Systemen interagiert, eine abstrakte Klasse im app/domain/ports Ordner. class ProductsRepository(ABC):
@abstractmethod
def add(self, product: product.Product) -> None:
...
class UnitOfWork(ABC):
products: ProductsRepository
@abstractmethod
def commit(self) -> None:
...
@abstractmethod
def __enter__(self) -> typing.Any:
...
@abstractmethod
def __exit__(self, *args) -> None:
...
Aktualisieren Sie die Befehlshandler-Signatur, sodass sie die neu deklarierten Abhängigkeiten akzeptiert, indem Sie die abstrakte Port-Klasse als Typanmerkung verwenden. def handle_create_product_command(
command: create_product_command.CreateProductCommand,
unit_of_work: unit_of_work.UnitOfWork,
) -> str:
...
Aktualisieren Sie den Komponententest, um das Verhalten aller deklarierten Abhängigkeiten für den Befehlshandler zu simulieren. # Arrange
mock_unit_of_work = unittest.mock.create_autospec(
spec=unit_of_work.UnitOfWork, instance=True
)
mock_unit_of_work.products = unittest.mock.create_autospec(
spec=unit_of_work.ProductsRepository, instance=True
)
Aktualisieren Sie die Assertionslogik im Test, um nach den erwarteten Abhängigkeitsaufrufen zu suchen. # Assert
mock_unit_of_work.commit.assert_called_once()
product = mock_unit_of_work.products.add.call_args.args[0]
assertpy.assert_that(product.name).is_equal_to("Test Product")
assertpy.assert_that(product.description).is_equal_to("Test Description")
Führen Sie den Komponententest aus, um zu sehen, ob er erfolgreich ist. python -m pytest
| App-Developer |
Schreiben Sie Integrationstests für sekundäre Adapter. | Erstellen Sie eine Testdatei in dem app/adapters/tests Ordner, indem Sie test_ sie als Dateinamenpräfix verwenden. Verwenden Sie die Moto-Bibliothek, um AWS Dienste nachzuahmen. @pytest.fixture
def mock_dynamodb():
with moto.mock_dynamodb():
yield boto3.resource("dynamodb", region_name="eu-central-1")
Erstellen Sie eine neue Testmethode für einen Integrationstest des Adapters. def test_add_and_commit_should_store_product(mock_dynamodb):
# Arrange
unit_of_work = dynamodb_unit_of_work.DynamoDBUnitOfWork(
table_name=TEST_TABLE_NAME, dynamodb_client=mock_dynamodb.meta.client
)
current_time = datetime.datetime.now(datetime.timezone.utc).isoformat()
new_product_id = str(uuid.uuid4())
new_product = product.Product(
id=new_product_id,
name="test-name",
description="test-description",
createDate=current_time,
lastUpdateDate=current_time,
)
# Act
with unit_of_work:
unit_of_work.products.add(new_product)
unit_of_work.commit()
# Assert
Erstellen Sie eine Adapterklasse im app/adapters Ordner. Verwenden Sie die abstrakte Klasse aus dem Ports-Ordner als Basisklasse. Führen Sie den Komponententest aus, um festzustellen, ob er fehlschlägt, da immer noch keine Logik vorhanden ist. python -m pytest
| App-Developer |
Implementieren Sie sekundäre Adapter. | Implementieren Sie Logik in der neu erstellten Adapterdatei. Aktualisieren Sie die Test-Assertionen. # Assert
with unit_of_work_readonly:
product_from_db = unit_of_work_readonly.products.get(new_product_id)
assertpy.assert_that(product_from_db).is_not_none()
assertpy.assert_that(product_from_db.dict()).is_equal_to(
{
"id": new_product_id,
"name": "test-name",
"description": "test-description",
"createDate": current_time,
"lastUpdateDate": current_time,
}
)
Führen Sie den Komponententest aus, um zu sehen, ob er erfolgreich ist. python -m pytest
| App-Developer |
Schreiben Sie end-to-end Tests. | Erstellen Sie eine Testdatei in dem app/entrypoints/api/tests Ordner, indem Sie test_ sie als Dateinamenpräfix verwenden. Erstellen Sie ein Lambda-Kontext-Fixture, das vom Test zum Aufrufen von Lambda verwendet wird. @pytest.fixture
def lambda_context():
@dataclass
class LambdaContext:
function_name: str = "test"
memory_limit_in_mb: int = 128
invoked_function_arn: str = "arn:aws:lambda:eu-west-1:809313241:function:test"
aws_request_id: str = "52fdfc07-2182-154f-163f-5f0f9a621d72"
return LambdaContext()
Erstellen Sie eine Testmethode für den Aufruf. API def test_create_product(lambda_context):
# Arrange
name = "TestName"
description = "Test description"
request = api_model.CreateProductRequest(name=name, description=description)
minimal_event = api_gateway_proxy_event.APIGatewayProxyEvent(
{
"path": "/products",
"httpMethod": "POST",
"requestContext": { # correlation ID
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef"
},
"body": json.dumps(request.dict()),
}
)
create_product_func_mock = unittest.mock.create_autospec(
spec=create_product_command_handler.handle_create_product_command
)
handler.create_product_command_handler.handle_create_product_command = (
create_product_func_mock
)
# Act
handler.handler(minimal_event, lambda_context)
Führen Sie den Komponententest aus, um festzustellen, ob er fehlschlägt, da immer noch keine Logik vorhanden ist. python -m pytest
| App-Developer |
Implementieren Sie Primäradapter. | Erstellen Sie eine Funktion für die API Geschäftslogik und deklarieren Sie sie als API Ressource. @tracer.capture_method
@app.post("/products")
@utils.parse_event(model=api_model.CreateProductRequest, app_context=app)
def create_product(
request: api_model.CreateProductRequest,
) -> api_model.CreateProductResponse:
"""Creates a product."""
...
Hinweis: Alle Decorators, die Sie sehen, sind Funktionen der AWS Lambda Powertools for Python-Bibliothek. Einzelheiten finden Sie auf der Website AWSLambda Powertools for Python. Implementieren Sie die LogikAPI. id=create_product_command_handler.handle_create_product_command(
command=create_product_command.CreateProductCommand(
name=request.name,
description=request.description,
),
unit_of_work=unit_of_work,
)
response = api_model.CreateProductResponse(id=id)
return response.dict()
Führen Sie den Komponententest aus, um zu sehen, ob er erfolgreich ist. python -m pytest
| App-Developer |
Zugehörige Ressourcen
APGLeitfaden
AWSVerweise
Tools
IDEs