Struktur proyek Python dalam arsitektur heksagonal menggunakan AWS Lambda - AWS Prescriptive Guidance

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

Struktur proyek Python dalam arsitektur heksagonal menggunakan AWS Lambda

Furkan Oruc, Dominik Goby, Darius Kunce, dan Michal Ploski, Amazon Web Services

Ringkasan

Pola ini menunjukkan bagaimana menyusun proyek Python dalam arsitektur heksagonal dengan menggunakan AWS Lambda. Pola ini menggunakan AWS Cloud Development Kit (AWS CDK) sebagai alat infrastruktur sebagai kode (IAc), Amazon API Gateway sebagai REST API, dan Amazon DynamoDB sebagai lapisan persistensi. Arsitektur heksagonal mengikuti prinsip-prinsip desain berbasis domain. Dalam arsitektur heksagonal, perangkat lunak terdiri dari tiga komponen: domain, port, dan adaptor. Untuk informasi rinci tentang arsitektur heksagonal dan manfaatnya, lihat panduan Membangun arsitektur heksagonal di AWS.

Prasyarat dan batasan

Prasyarat

Versi produk

  • Git versi 2.24.3 atau yang lebih baru

  • Python versi 3.7 atau yang lebih baru

  • AWS CDK v2

  • Puisi versi 1.1.13 atau yang lebih baru

  • AWS Lambda Powertools untuk Python versi 1.25.6 atau yang lebih baru

  • pytest versi 7.1.1 atau yang lebih baru

  • Moto versi 3.1.9 atau yang lebih baru

  • pydantic versi 1.9.0 atau yang lebih baru

  • Boto3 versi 1.22.4 atau yang lebih baru

  • mypy-boto3-dynamodb versi 1.24.0 atau yang lebih baru

Arsitektur

Tumpukan teknologi target

Tumpukan teknologi target terdiri dari layanan Python yang menggunakan API Gateway, Lambda, dan DynamoDB. Layanan ini menggunakan adaptor DynamoDB untuk mempertahankan data. Ini menyediakan fungsi yang menggunakan Lambda sebagai titik masuk. Layanan ini menggunakan Amazon API Gateway untuk mengekspos REST API. API menggunakan AWS Identity and Access Management (IAM) untuk otentikasi klien.

Arsitektur target

Untuk mengilustrasikan implementasi, pola ini menerapkan arsitektur target tanpa server. Klien dapat mengirim permintaan ke titik akhir API Gateway. API Gateway meneruskan permintaan ke fungsi Lambda target yang mengimplementasikan pola arsitektur heksagonal. Fungsi Lambda melakukan operasi create, read, update, and delete (CRUD) pada tabel DynamoDB.

penting

Pola ini diuji di lingkungan PoC. Anda harus melakukan tinjauan keamanan untuk mengidentifikasi model ancaman dan membuat basis kode yang aman sebelum Anda menerapkan arsitektur apa pun ke lingkungan produksi.

Arsitektur target untuk penataan proyek Python dalam arsitektur heksagonal

API mendukung lima operasi pada entitas produk:

  • GET /productsmengembalikan semua produk.

  • POST /productsmenciptakan produk baru.

  • GET /products/{id}mengembalikan produk tertentu.

  • PUT /products/{id}memperbarui produk tertentu.

  • DELETE /products/{id}menghapus produk tertentu.

Anda dapat menggunakan struktur folder berikut untuk mengatur proyek Anda untuk mengikuti pola arsitektur heksagonal:  

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

Alat

Layanan AWS

  • Amazon API Gateway adalah layanan yang dikelola sepenuhnya yang memudahkan pengembang untuk membuat, menerbitkan, memelihara, memantau, dan mengamankan APIs pada skala apa pun.

  • Amazon DynamoDB adalah database NoSQL bernilai kunci yang dikelola sepenuhnya, tanpa server, yang dirancang untuk menjalankan aplikasi berkinerja tinggi pada skala apa pun.

  • AWS Lambda adalah layanan komputasi tanpa server dan berbasis peristiwa yang memungkinkan Anda menjalankan kode untuk hampir semua jenis aplikasi atau layanan backend tanpa menyediakan atau mengelola server. Anda dapat meluncurkan fungsi Lambda dari lebih dari 200 layanan AWS dan aplikasi perangkat lunak sebagai layanan (SaaS), dan hanya membayar untuk apa yang Anda gunakan.

Alat

  • Git digunakan sebagai sistem kontrol versi untuk pengembangan kode dalam pola ini.

  • Python digunakan sebagai bahasa pemrograman untuk pola ini. Python menyediakan struktur data tingkat tinggi dan pendekatan untuk pemrograman berorientasi objek. AWS Lambda menyediakan runtime Python bawaan yang menyederhanakan pengoperasian layanan Python.

  • Visual Studio Code digunakan sebagai IDE untuk pengembangan dan pengujian untuk pola ini. Anda dapat menggunakan IDE apa pun yang mendukung pengembangan Python (misalnya, PyCharm).

  • AWS Cloud Development Kit (AWS CDK) adalah kerangka kerja pengembangan perangkat lunak open-source yang memungkinkan Anda menentukan sumber daya aplikasi cloud Anda dengan menggunakan bahasa pemrograman yang sudah dikenal. Pola ini menggunakan CDK untuk menulis dan menyebarkan infrastruktur cloud sebagai kode.

  • Puisi digunakan untuk mengelola dependensi dalam pola.

  • Docker digunakan oleh AWS CDK untuk membangun paket dan lapisan Lambda.

Kode

Kode untuk pola ini tersedia di repositori sampel arsitektur heksagonal GitHub Lambda.

Praktik terbaik

Untuk menggunakan pola ini dalam lingkungan produksi, ikuti praktik terbaik berikut:

Pola ini menggunakan AWS X-Ray untuk melacak permintaan melalui titik masuk, domain, dan adaptor aplikasi. AWS X-Ray membantu pengembang mengidentifikasi kemacetan dan menentukan latensi tinggi untuk meningkatkan kinerja aplikasi.

Epik

TugasDeskripsiKeterampilan yang dibutuhkan

Buat repositori Anda sendiri.

  1. Masuk ke GitHub.

  2. Buat repositori baru. Untuk instruksi, lihat GitHub dokumentasi.

  3. Kloning dan dorong repositori sampel untuk pola ini ke dalam repositori baru di akun Anda.

Pengembang aplikasi

Instal dependensi.

  1. Instal Puisi.

    pip install poetry
  2. Instal paket dari direktori root. Perintah berikut menginstal aplikasi dan paket AWS CDK. Ini juga menginstal paket pengembangan yang diperlukan untuk menjalankan pengujian unit. Semua paket yang diinstal ditempatkan di lingkungan virtual baru.

    poetry install
  3. Untuk melihat representasi grafis dari paket yang diinstal, jalankan perintah berikut.

    poetry show --tree
  4. Perbarui semua dependensi.

    poetry update
  5. Buka shell baru dalam lingkungan virtual yang baru dibuat. Ini berisi semua dependensi yang diinstal.

    poetry shell
Pengembang aplikasi

Konfigurasikan IDE Anda.

Kami merekomendasikan Visual Studio Code, tetapi Anda dapat menggunakan IDE pilihan Anda yang mendukung Python. Langkah-langkah berikut adalah untuk Visual Studio Code.

  1. Perbarui .vscode/settings file.

    { "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", }
  2. Buat .env file di direktori root proyek. Ini memastikan bahwa direktori root proyek disertakan dalam PYTHONPATH sehingga pytest dapat menemukannya dan menemukan semua paket dengan benar.

    PYTHONPATH=.
Pengembang aplikasi

Jalankan pengujian unit, opsi 1: Gunakan Kode Visual Studio.

  1. Pilih penerjemah Python dari lingkungan virtual yang dikelola oleh Puisi.

  2. Jalankan tes dari Test Explorer.

Pengembang aplikasi

Jalankan pengujian unit, opsi 2: Gunakan perintah shell.

  1. Mulai shell baru dalam lingkungan virtual.

    poetry shell
  2. Jalankan pytest perintah dari direktori root.

    python -m pytest

    Atau Anda dapat menjalankan perintah langsung dari Puisi.

    poetry run python -m pytest
Pengembang aplikasi
TugasDeskripsiKeterampilan yang dibutuhkan

Minta kredensi sementara.

Untuk memiliki kredensi AWS di shell saat Anda menjalankancdk deploy, buat kredensil sementara dengan menggunakan AWS IAM Identity Center (penerus AWS Single Sign-On). Untuk petunjuknya, lihat posting blog Cara mengambil kredensi jangka pendek untuk penggunaan CLI dengan AWS IAM Identity Center.

Pengembang aplikasi, AWS DevOps

Men-deploy aplikasi.

  1. Instal AWS CDK v2.

    npm install -g aws-cdk

    Untuk informasi selengkapnya, lihat dokumentasi AWS CDK.

  2. Bootstrap AWS CDK ke akun dan Wilayah Anda.

    cdk bootstrap aws://12345678900/us-east-1 --profile aws-profile-name
  3. Terapkan aplikasi sebagai CloudFormation tumpukan AWS dengan menggunakan profil AWS.

    cdk deploy --profile aws-profile-name
Pengembang aplikasi, AWS DevOps

Uji API, opsi 1: Gunakan konsol.

Gunakan konsol API Gateway untuk menguji API. Untuk informasi selengkapnya tentang operasi dan request/response pesan API, lihat bagian penggunaan API dari file readme di GitHub repositori.

Pengembang aplikasi, AWS DevOps

Uji API, opsi 2: Gunakan Tukang Pos.

Jika Anda ingin menggunakan alat seperti Tukang Pos:

  1. Instal Postman sebagai aplikasi mandiri atau ekstensi browser.

  2. Salin URL titik akhir untuk API Gateway. Ini akan dalam format berikut.

    https://{api-id}.execute-api.{region}.amazonaws.com/{stage}/{path}
  3. Konfigurasikan tanda tangan AWS di tab otorisasi. Untuk petunjuknya, lihat artikel AWS re:Post tentang mengaktifkan autentikasi IAM untuk API Gateway REST. APIs

  4. Gunakan Postman untuk mengirim permintaan ke titik akhir API Anda.

Pengembang aplikasi, AWS DevOps
TugasDeskripsiKeterampilan yang dibutuhkan

Tulis tes unit untuk domain bisnis.

  1. Buat file Python di app/domain/tests folder dengan menggunakan awalan nama test_ file.

  2. Buat metode pengujian baru untuk menguji logika bisnis baru dengan menggunakan contoh berikut.

    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
  3. Buat kelas perintah di app/domain/commands folder. 

  4. Jika fungsionalitasnya baru, buat rintisan untuk pengendali perintah di folder. app/domain/command_handlers

  5. Jalankan unit test untuk melihatnya gagal, karena masih belum ada logika bisnis.

    python -m pytest
Pengembang aplikasi

Menerapkan perintah dan penangan perintah.

  1. Menerapkan logika bisnis dalam file handler perintah yang baru dibuat. 

  2. Untuk setiap dependensi yang berinteraksi dengan sistem eksternal, deklarasikan kelas abstrak di folder. app/domain/ports

    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: ...
  3. Perbarui tanda tangan pengendali perintah untuk menerima dependensi yang baru dideklarasikan dengan menggunakan kelas port abstrak sebagai anotasi tipe.

    def handle_create_product_command( command: create_product_command.CreateProductCommand, unit_of_work: unit_of_work.UnitOfWork, ) -> str: ...
  4. Perbarui pengujian unit untuk mensimulasikan perilaku semua dependensi yang dideklarasikan untuk pengendali perintah.

    # 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 )
  5. Perbarui logika pernyataan dalam pengujian untuk memeriksa pemanggilan ketergantungan yang diharapkan.

    # 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")
  6. Jalankan unit test untuk melihatnya berhasil.

    python -m pytest
Pengembang aplikasi

Tulis tes integrasi untuk adaptor sekunder.

  1. Buat file uji di app/adapters/tests folder dengan menggunakan test_ sebagai awalan nama file.

  2. Gunakan pustaka Moto untuk mengejek layanan AWS.

    @pytest.fixture def mock_dynamodb(): with moto.mock_dynamodb(): yield boto3.resource("dynamodb", region_name="eu-central-1")
  3. Buat metode pengujian baru untuk uji integrasi adaptor.

    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
  4. Buat kelas adaptor di app/adapters folder. Gunakan kelas abstrak dari folder port sebagai kelas dasar.

  5. Jalankan unit test untuk melihatnya gagal, karena masih belum ada logika.

    python -m pytest
Pengembang aplikasi

Menerapkan adaptor sekunder.

  1. Menerapkan logika dalam file adaptor yang baru dibuat.

  2. Perbarui pernyataan uji.

    # 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, } )
  3. Jalankan unit test untuk melihatnya berhasil.

    python -m pytest
Pengembang aplikasi

Tulis end-to-end tes.

  1. Buat file uji di app/entrypoints/api/tests folder dengan menggunakan test_ sebagai awalan nama file. 

  2. Buat perlengkapan konteks Lambda yang akan digunakan oleh tes untuk memanggil Lambda.

    @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()
  3. Buat metode pengujian untuk pemanggilan 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)
  4. Jalankan unit test untuk melihatnya gagal, karena masih belum ada logika.

    python -m pytest
Pengembang aplikasi

Menerapkan adaptor utama.

  1. Buat fungsi untuk logika bisnis API dan deklarasikan sebagai sumber daya API.

    @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.""" ...
    catatan

    Semua dekorator yang Anda lihat adalah fitur dari AWS Lambda Powertools untuk pustaka Python. Untuk detailnya, lihat situs web AWS Lambda Powertools for Python.

  2. Menerapkan logika API.

    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()
  3. Jalankan unit test untuk melihatnya berhasil.

    python -m pytest
Pengembang aplikasi

Sumber daya terkait

Panduan APG

Referensi AWS

Alat

IDEs