<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: BrycePC</title>
    <description>The latest articles on Forem by BrycePC (@brycepc).</description>
    <link>https://forem.com/brycepc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F700768%2F3b5f8d1b-0db8-4347-a45b-09bf11be0fd7.jpg</url>
      <title>Forem: BrycePC</title>
      <link>https://forem.com/brycepc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/brycepc"/>
    <language>en</language>
    <item>
      <title>AWS Devops Agent - AI-Based Incident Analysis Demo with "The Better Store"</title>
      <dc:creator>BrycePC</dc:creator>
      <pubDate>Sat, 11 Apr 2026 09:02:04 +0000</pubDate>
      <link>https://forem.com/aws-builders/aws-devops-agent-ai-based-incident-analysis-demo-with-the-better-store-28ok</link>
      <guid>https://forem.com/aws-builders/aws-devops-agent-ai-based-incident-analysis-demo-with-the-better-store-28ok</guid>
      <description>&lt;p&gt;In a previous series of articles I have described the design and implementation of a sample ECommerce serverless solution on AWS at: &lt;a href="https://dev.to/brycepc/building-the-better-store-an-agile-cloud-native-ecommerce-system-on-aws-part-1-introduction-to-27ii"&gt;https://dev.to/brycepc/building-the-better-store-an-agile-cloud-native-ecommerce-system-on-aws-part-1-introduction-to-27ii&lt;/a&gt;. The solution's source code is also available at: &lt;a href="https://github.com/TheBetterStore" rel="noopener noreferrer"&gt;https://github.com/TheBetterStore&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article I explore the new AWS DevOps Agent service which has just become generally available in March 2026, to demonstrate its AI capabilities for automatic root cause analysis of alarm-notified incidents using all sources of information that it has available to it, including (but not limited to) Cloudtrail events, API Gateway, application and database logs and metrics, x-ray, its determined implementation topology, and integrated source code repositories - using errors introduced into The Better Store. The produced results as discussed in the conclusion certainly look impressive, for quickly analyzing errors in detail which could otherwise take much manual effort! &lt;/p&gt;




&lt;h2&gt;
  
  
  Initial Setup
&lt;/h2&gt;

&lt;p&gt;DevOps Agent may be considered as a global service within AWS in that it monitors resources across all regions; though noting at the current date the service's control plane for configuration is available in 6 regions only (us-east-1, us-west-2, ap-southeast-2, ap-northeast-1, eu-central-1, eu-west-1). Its initial setup is relatively simple; it involves primarily:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating an AWS Devops Agent space to define an application boundary to monitor. AWS allows creation of multiple agent spaces within an account primarily to support segregating investigation scope to team ownership; where each on-call team has an Agent Space containing only the accounts (if cross-account access for an application is in-play) and tools relevant to what they're responsible for.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsz2wd5fgqw32g8o8jg9e.png" alt="Agent Space Setup" width="800" height="460"&gt;
&lt;/li&gt;
&lt;li&gt;Configuring capabilities including:

&lt;ol&gt;
&lt;li&gt;Secondary sources; e.g. other AWS accounts where cross-account resources are used, or Azure accounts where Azure resources may also be integrated for DevOps Agent monitoring.&lt;/li&gt;
&lt;li&gt;Telemetry - external sources including Dynatrace, Datadog, Grafana, New Relic and Splunk may be integrated.&lt;/li&gt;
&lt;li&gt;Pipeline; where source code repositories for applications being monitored may be configured to support Devops Agent Analyses.&lt;/li&gt;
&lt;li&gt;MCP Servers to support agentic retrieval against additional sources of information which may be pertinent to an investigation. For example, integration of Atlassian MCP servers are supported, which can provide AWS Devops Agent with additional information such as runbooks from an organization's Confluence pages.&lt;/li&gt;
&lt;li&gt;Webhooks; to allow 2-way access between an Agent Space and 3rd-party applications and services.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Configuring access, including Operator access which support teams will use for interacting with DevOps Agent and its analyses. By default, operator access is available to users as a short-term link from the AWS web console which they require access to. Alternatively access can be integrated with IAM Identity Center or an external identity provider.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Configuring log delivery.&lt;br&gt;
For our demonstration with TheBetterStore we will be configuring the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pipeline - GitHub repositories used for TheBetterStore's services.&lt;/li&gt;
&lt;li&gt;Webhooks - A separate lambda function as defined at tbs-devops-aiagent will be subscribed to error alarms configured for TheBetterStore resources, and on being triggered it will invoke a webhook defined by AWS Devops Agent, passing the alarm information to trigger an incident investigation.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0n2cdbtq97l41gj82fd3.png" alt="Webhook setup" width="800" height="495"&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Demo: Incident Simulation and DevOps Agent Analysis
&lt;/h2&gt;

&lt;p&gt;For the demonstration, a bug was deliberately introduced into a new code branch in the tbs-app-order GitHub repository, to invalidate the key used for inserting data into a dynamodb table. This would cause an error to be thrown in the tbs-app-order-prod lambda services when a user tries to purchase an item from TheBetterStore, triggering an alarm. The sequence of events are illustrated as:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;1. Bug introduced&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fppwj5zeeqwhciud4bw8j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fppwj5zeeqwhciud4bw8j.png" alt="Error CodeChange in Github" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;2. Feature is deployed via GitHub Actions Pipeline&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fflusuwym2bckzzw7fpcg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fflusuwym2bckzzw7fpcg.png" alt="GitHub Actions Deployment" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;3. TheBetterStore error is generated via a purchase:&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy3ofbjmyqcppe2xatv1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy3ofbjmyqcppe2xatv1k.png" alt="Trigger TheBetterStore error" width="800" height="733"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;4. Cloudwatch alarm is triggered&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrezhpp3gjvha0lk727x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrezhpp3gjvha0lk727x.png" alt="Cloudwatch Alarm" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;5. Devops Agent Incident Is Created&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghgycpei0yu433iabmhz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghgycpei0yu433iabmhz.png" alt="Incident Created" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Devops Incident Response Analysis
&lt;/h2&gt;

&lt;p&gt;Incidents generated by Devops Agent are presented within the Operators web  console in 3 separate tabs; an investigation timeline, root cause analysis (RCA), and a mitigation plan. Additionally, the Operators web console always provides a Chat window, whereby ad-hoc questions may be asked, with prompts for suggested questions also being provided (e.g. 'Summarise the investigation). Each of these sections are described below&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Investigation timeline&lt;/strong&gt;&lt;br&gt;
Information within this tab describes how DevOps Agent queries underlying resources for its analysis. The first item that is displayed is a topology diagram for AWS-deployed resources and their relationships which may be in-scope for the analysis. This information is automatically-discovered by AWS Devops Agent, and it may assist with its analysis from the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identifies in-scope components and blast radius&lt;/li&gt;
&lt;li&gt;Traces impact paths through dependency chains&lt;/li&gt;
&lt;li&gt;Helps pinpoint root cause&lt;/li&gt;
&lt;li&gt;Shows what downstream services may be affected.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For our incident, the following is presented&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fztlyez602nusbll42owl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fztlyez602nusbll42owl.png" alt="Topology" width="800" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, a timeseries of analysis events are listed which details how Devops Agent queries different sources of information to reach its findings. Only a subset of these are listed below, as an example.&lt;/p&gt;

&lt;p&gt;10:30:26 Started&lt;/p&gt;

&lt;p&gt;10:30:48. Fetching Data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Now I have a clear picture:

Alarm: tbs-app-order-prod-CreateOrder-Errors triggered at 2026-04-10T22:29:04Z
Metric: AWS/Lambda Errors for function tbs-app-order-prod-CreateOrderFunction-BZMzl0lYouog
Datapoint: 1 error at 2026-04-10T22:24:00Z (5-minute period)
Account: 048116471576, region: ap-southeast-2
The Order Service has:

API GW: v6jpsjputa
Lambda: CreateOrderFunction-BZMzl0lYouog
DynamoDB: tbs-app-order-prod-OrderTable-QYMTFX1MFY1E

Repository: TheBetterStore/tbs-app-order

I need to start investigating by checking the Lambda logs for what went wrong, looking at recent deployments or code changes, reviewing the Lambda metrics, checking CloudTrail for any infrastructure modifications, and examining the dependencies like DynamoDB and Stripe integration.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;10:31:40 Fetching data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Now let me run multiple CloudTrail searches in parallel. I'll search by resource name for the key resources, and also search by event source for CloudFormation and CodeDeploy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;10:31:55 Fetching data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The actual log group name is /aws/lambda/tbs-app-order-prod-CreateOrderFunction (without the suffix). Let me query it now:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;10:32:00 Fetching data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let me now check GitHub workflow runs and CodeDeploy deployments:
1. Batch get deployments
2. Get github workflow run logs
3. Git log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;10:32:28 Fetching data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The most recent successful deployment has a commit message "Introduce bug" — that's highly suspicious! Let me dig deeper:

1. Batch get deployments
2. Get github workflow run logs
3. Git log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;10:32:42 Fetching data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Now let me look at the actual code changes in the "Introduce bug" commit:

Git log
Get github file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;10:32:54 Observation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DynamoDB ValidationException: Missing the key OrderId in the item during CreateOrder invocations
Two CreateOrder Lambda invocations at 22:27:24Z and 22:28:23Z both failed with the same DynamoDB ValidationException: "One or more parameter values were invalid: Missing the key OrderId in the item". This indicates the Lambda code is attempting a DynamoDB PutItem (or similar) operation without including the required partition key "OrderId". Both invocations followed successful Stripe PaymentIntent creation (with orderId metadata set), suggesting the error occurs between payment intent creation and DynamoDB persistence. The Lambda platform reports both invocations as "success" (status code), meaning the function caught the error rather than crashing—but the application-level ERROR log triggered the CloudWatch alarm. Notably, both invocations used 512MB of the 512MB allocated memory, and the baseline window had zero invocations, so this is not a pre-existing pattern.
DynamoDB ValidationException thrown twice: 'Missing the key OrderId in the item' — the Lambda function is writing to DynamoDB without the required partition key
Source: cloudwatch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;10:34:14 Root cause&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deployment of commit 4ba4473 renamed OrderId to OrderIdX in DynamoDB DTO mapping, breaking all CreateOrder writes
At 2026-04-10T07:16-07:19Z, GitHub Actions workflow run #24231398661 deployed commit 4ba4473 ('Introduce bug') from branch feature/aiagenttest to the CreateOrder Lambda function via CodeDeploy Blue/Green (deployment d-Q063WA0TH). The commit modified src/infrastructure/persistence/order-repository.ts, renaming the DynamoDB partition key field from 'OrderId' to 'OrderIdX' in the static toDto() method. Since 'OrderId' is the required partition key of the OrderTable (tbs-app-order-prod-OrderTable-QYMTFX1MFY1E), every DynamoDB PutItem call now fails with ValidationException because the item is missing the partition key. This causes 100% of CreateOrder invocations to fail. The deployment occurred ~15 hours before the first error was observed at 22:27Z, indicating no CreateOrder requests were made in the intervening period.

Investigation completed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause&lt;/strong&gt;&lt;br&gt;
Information within this tab summarises the investigation and timeline findings, to include the incident impact, root cause(s) and key findings&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2pg51pjxsbh3qyma5ff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2pg51pjxsbh3qyma5ff.png" alt=" " width="800" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mitigation Plan&lt;/strong&gt;&lt;br&gt;
Information here focuses on steps to resolve the incident. For our example the root cause was identified as being due to a bug being introduced into our GitHub source code repository. Steps are provided on how to revert the change/rollback the errant commit and to redeploy the solution to AWS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2wb6t3oyska7elqg3gd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2wb6t3oyska7elqg3gd.png" alt=" " width="800" height="704"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7m26bec8rmschvkbqr64.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7m26bec8rmschvkbqr64.png" alt=" " width="800" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg48v22p4acfjtvtnfbdg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg48v22p4acfjtvtnfbdg.png" alt=" " width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsq72jznr4erbz4zpnwqz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsq72jznr4erbz4zpnwqz.png" alt=" " width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Above steps also included sample AWS CLI commands that could be run to validate deployments and final service health (though it did specify the incorrect region for our services for our test (as of April 2026; it is likely to be resolved in a future update).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summarise The Investigation&lt;/strong&gt;&lt;br&gt;
Finally, use of the Chat feature; specifically the prompt 'Summarise the Investigation' was tried here, which provided the following results:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19ewgnmgyqo206ats7u7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19ewgnmgyqo206ats7u7.png" alt=" " width="800" height="787"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This gave additional insights to the impact of the issue that could be helpful; including the amount of the failed orders and note of a reconciliation gap with Stripe, to be amended. The chat window also suggested other deeper queries that we could consider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This demonstration highlighted how a Cloudwatch alarm-triggered incident was able to invoke its automated analysis by Devops Agent, which used multiple sources of information available to it to generate accurate mitigation steps and insights in less than 4 minutes from its commencement. Its analysis involved examination of multiple sources of information, including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cloudtrail events, as to whether infrastructure for any resources had been updated (they hadn't).&lt;/li&gt;
&lt;li&gt;Examining the topology to understand dependencies and architecture (and potential dowstream effects) of resources. This identified that there could be a reconciliation gap between our DynamoDB and Stripe (card payments provider)!&lt;/li&gt;
&lt;li&gt;Examining recent deployments of the affected resource in Cloudformation, which is used for its deployment.&lt;/li&gt;
&lt;li&gt;Examining the impact lambda's application logs in Cloudwatch.&lt;/li&gt;
&lt;li&gt;Examining GitHub deployments, and associated code changes.&lt;/li&gt;
&lt;li&gt;Checking for errors prior to the change (to confirm the errors are new).&lt;/li&gt;
&lt;li&gt;Recognising that the causing git commit was highly suspicious (though noting this code commit comment was entered for the demonstration).&lt;/li&gt;
&lt;li&gt;Concluding that the issue was caused by the code change, for inserting records into a DynamoDB table with the wrong key; but also noting that the  lambda was using all of its allocated memory in the process, as an additional issue to be addressed!&lt;/li&gt;
&lt;li&gt;Generating root cause analysis, and mitigation steps.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without this automation, it is envisaged that a competent support engineer who is familiar with the application would be able to identify the issue by first examining the lambda's application logs (while its errors are the source of the alarm) to see the error details. However at this point they may not be aware of the lambda's recent deployment; if not it is expected that they would need to liaise with the lambda's development team to identify and resolve the issue. The turnaround for this from an engineer first receiving an alarm would typically range from 30 minutes to 1-3 hours; during which in this example the system could be unavailable to users! It is unlikely that analysis at this time would also identify a potential memory issue, and coverage of an impact assessment for example to include gap analysis formed with Stripe would be dependent on maturity of support processes in place.&lt;br&gt;
It is also anticipated that Devops Agent would be well-placed for identifying more obscure issues where it is able to quickly select and scan required sources of information; for example slow performance due to slow RDS SQL queries leading to lambda or API Gateway times; or a new Service Control Policy being applied to the AWS Account which unexpectedly result in permission errors. Such problems typically require time and AWS specialists to resolve.&lt;/p&gt;

&lt;p&gt;The benefits of AWS Devops Agent for incident analysis hence look fantastic, and the ability to add MCP Servers can also provide further information enrichment capabilities to organisations. However there are a couple of factors that should be considered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SRE maturity: The described solution is dependent on Cloudwatch alarms being appropriately configured, to trigger incident investigations when there is a real problem.&lt;/li&gt;
&lt;li&gt;Cost: Charges are based on the time that the agent spends on operation tasks such as investigations and chat queries, billed by the second. For deployments to us-east-1 this is $0.0083USD per agent second. For an investigation taking 4 minutes to execute (similar to our demonstration), this would equate to $1.992USD. As can be imagined charges could accumulate rapidly if alarms are frequent! AWS Devops Agent however at this time is included in the free tier plan for new customers, and a free trial is also provided to customers for the first 2 months after first use of the service. Credits are also provided to customers on paid AWS Support plans. The &lt;a href="https://aws.amazon.com/devops-agent/pricing/" rel="noopener noreferrer"&gt;AWS DevOps Pricing&lt;/a&gt; page should always be consulted for latest reliable information.&lt;/li&gt;
&lt;li&gt;Security: It is good to know that AWS Devops Agent provides read-only actions against its AWS Accounts to support queries and investigations; it does not support update actions for example for remediation. There are however still important security considerations to be aware of, to ensure AWS-stored data is not leaked beyond intended audiences and an organisation's security policies are maintained. These include:

&lt;ol&gt;
&lt;li&gt;Separate agent spaces should be created with their own IAM roles to maintain isolation between application boundaries, and to prevent unintended access across different environments or teams. Authorisation should be configured for each Devops Agent space via integration with IAM Identity Center or other external provider to ensure only allowed users can access these.&lt;/li&gt;
&lt;li&gt;While AWS Devops Agent provides prompt injection safeguards to ensure read-only capabilities beyond opening of tickets and support cases, integrated external MCP servers may offer the same protection, and these should be carefully reviewed before enabling.
AWS provides further security recommendations for AWS Devops Agent, which can be reviewed &lt;a href="https://docs.aws.amazon.com/devopsagent/latest/userguide/aws-devops-agent-security.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What are DevOps Agent Spaces? - &lt;a href="https://docs.aws.amazon.com/devopsagent/latest/userguide/about-aws-devops-agent-what-are-devops-agent-spaces.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/devopsagent/latest/userguide/about-aws-devops-agent-what-are-devops-agent-spaces.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; Best Practices for Deploying AWS DevOps Agent in Production - &lt;a href="https://aws.amazon.com/blogs/devops/best-practices-for-deploying-aws-devops-agent-in-production/" rel="noopener noreferrer"&gt;https://aws.amazon.com/blogs/devops/best-practices-for-deploying-aws-devops-agent-in-production/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;What is a DevOps Agent Topology? - &lt;a href="https://docs.aws.amazon.com/devopsagent/latest/userguide/about-aws-devops-agent-what-is-a-devops-agent-topology.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/devopsagent/latest/userguide/about-aws-devops-agent-what-is-a-devops-agent-topology.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS DevOps Agent Security - &lt;a href="https://docs.aws.amazon.com/devopsagent/latest/userguide/aws-devops-agent-security.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/devopsagent/latest/userguide/aws-devops-agent-security.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Disclaimer: The views and opinions expressed in this article are those of the author only.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devopsagent</category>
      <category>sre</category>
    </item>
    <item>
      <title>Building “The Better Store” an agile cloud-native ecommerce system on AWS — Part 1: Introduction to Microservice Architecture</title>
      <dc:creator>BrycePC</dc:creator>
      <pubDate>Sat, 11 Apr 2026 02:44:23 +0000</pubDate>
      <link>https://forem.com/brycepc/building-the-better-store-an-agile-cloud-native-ecommerce-system-on-aws-part-1-introduction-to-27ii</link>
      <guid>https://forem.com/brycepc/building-the-better-store-an-agile-cloud-native-ecommerce-system-on-aws-part-1-introduction-to-27ii</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futotgb7082glvn2s5fzk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futotgb7082glvn2s5fzk.png" alt=" " width="720" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;A resilient cloud native app built using Domain Driven Design and Microservice Architecture on AWS&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;A lot has changed in IT over the last 10–15 years, with the advent of Cloud Native Computing. Its rise has enabled organisations from the smallest of start-ups to large organisations to design and implement systems that can:&lt;/p&gt;

&lt;p&gt;Automatically scale on demand (and paying only for what you use).&lt;br&gt;
Provide system resilience; i.e. such that a service can recover on failure.&lt;br&gt;
Support change agility; i.e. to allow the implementation of new features within a short timeframe with minimal risk of outage.&lt;/p&gt;

&lt;p&gt;As a Senior Consultant with several years experience specialising in Cloud-Native design, full-stack development and DevSecOps implementation on AWS within both start-up and large organisations, there has been much to keep up with and learn as new methodologies have emerged predominantly around Microservice Architectures (MSA) and Cloud Native patterns. Many of these topics are still largely debated; e.g. how big should a microservice be? Should microservices be allowed to communicate with other microservices, and are shared databases across microservices okay?&lt;/p&gt;

&lt;p&gt;As an endeavour to expand and share my own knowledge of the topics with examples to demonstrate and build upon, “The Better Store” has been developed as a sample open-source eCommerce system with web (SPA) frontend to illustrate Domain Driven Design (DDD), MSA, Cloud Native development and implementation on AWS, with the intention of evolving this over time. This article is Part One of a series of seven planned articles, which will cover the following topics with reference to The Better Store:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part One:&lt;/strong&gt; An introduction to Microservices Architecture (MSA) to realize benefits of Cloud Native Computing, and Domain Driven Design (DDD) as a popular to assist in the formulation of an optimum MSA solution design for our given business domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part Two:&lt;/strong&gt; A focus on DDD Strategic Patterns, to assist in defining our business domain and appropriate Bounded Contexts, which help identify candidate microservices.&lt;/p&gt;

&lt;p&gt;e.g. The Better Store context map&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn50sw25xmqkz2ix0jb2l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn50sw25xmqkz2ix0jb2l.png" alt=" " width="720" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Figure 1. Sample Context Map for “The Better Store”, providing insights to potential decoupled microservices and their granularity/scope.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part Three:&lt;/strong&gt; Using DDD Tactical Patterns for further elaboration of The Better Store’s domain model and MSA design, with a focus on its Order Context.&lt;/p&gt;

&lt;p&gt;e.g.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh967eya0qigzsxhutu1o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh967eya0qigzsxhutu1o.png" alt="Orders class diagram" width="720" height="697"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Figure 2: Illustrating a potential static view as a class diagram for designing an Order microservice, showing Domain (core logic), Infrastructure and Application Tiers&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part Four:&lt;/strong&gt; Selecting Cloud Native Design patterns and AWS Services for implementation of The Better Store as an agile, highly scalable and resilient global solution; including at a high-level:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Decoupled services and communication styles&lt;/li&gt;
&lt;li&gt;Database per Service&lt;/li&gt;
&lt;li&gt;CQRS, and related data consistency patterns (i.e. Sagas, Aggregates and Event Driven Architecture)&lt;/li&gt;
&lt;li&gt;API Gateway and composition&lt;/li&gt;
&lt;li&gt;Global and auto-scaling architecture&lt;/li&gt;
&lt;li&gt;Serverless implementation using AWS API Gateway and Lambda with NodeJS, Typescript, Inversify and the Onion Architecture&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1581t3vadykcsh9298b7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1581t3vadykcsh9298b7.png" alt="Solution implementation view" width="800" height="497"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 3: An envisaged implementation view of The Better Store&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part Five:&lt;/strong&gt; Use of DevOps; specifically AWS Cloudformation combined with GitHub and AWS Pipelines for defining both applications and infrastructure as code, for fully-automated deployments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part Six:&lt;/strong&gt; Development of the web frontend as a Single Page Application, using a javascript framework&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9e5j8xmqvwhi3aqic3qi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9e5j8xmqvwhi3aqic3qi.png" alt="Website landing page" width="720" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Figure 4: The Better Store home page, whereby users may browse products, and after signup or sign-in, may add these to cart and purchase (using test payment services only)&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;We envisage that our final MSA landscape may look like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98zvanlxfh9dhfo8ylez.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98zvanlxfh9dhfo8ylez.png" alt="Conceptual architecture" width="720" height="687"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 5: An envisaged conceptual architecture for The Better Store, illustrating decoupled microservices&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part Seven:&lt;/strong&gt; Monitoring and Site Reliability Engineering; how to get the most out of AWS Cloudwatch for early identification of potential issues and remediation, scaling and obtaining business insights.&lt;/p&gt;

&lt;p&gt;For Part One though, we’ll now look further at a microservices introduction, its challenges, and then what we would like to initially see for The Better Store.&lt;/p&gt;

&lt;h2&gt;
  
  
  An introduction to Microservices
&lt;/h2&gt;

&lt;p&gt;One of the most well-known methodologies adopted in recent years to help realize Agility, Scalability and Resilience capabilities in The Cloud, particularly for medium to enterprise-sized applications is Microservice Architectures (MSA). MSA takes the approach of designing applications as a composite of small, decoupled, stateless and independent services that are cohesive to a specific task, to provide the following advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Services are kept small and cohesive to a specific function, to promote Change Agility, and Scalability.&lt;/strong&gt;&lt;br&gt;
If a change is required to a specific application feature, e.g. cancelling an eCommerce order; only one independently-deployable service; e.g. Order Service that is specific to maintaining orders should require changing at the backend. This helps keep logic clear in one place, and the risk of a severe or unforeseen release defect is reduced when only 1 component needs to be released. This reduced change size and risk can (and should) allow changes to be deployed faster with reduced change management, allowing consumers to benefit from changes more quickly (while also further reducing risk as new changes are deployed in small increments). Smaller-sized services can also generally be started quicker by the Cloud platform when configured for horizontal scaling (i.e. starting multiple instances of a service to handle increased demand).&lt;br&gt;
Note that adoption of MSA to compose an application as many microservices does add a level of complexity to the solution, especially concerning their interaction and when multiple services are being developed concurrently. Releasing small-sized changes frequently with the support of mature DevOps processes for automated testing and deployment is often considered a prerequisite, which requires initial investment for organisations that wish to adopt MSA.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Services are decoupled; to promote Change Agility, Scalability and Resilience, and be Technology Agnostic&lt;/strong&gt;&lt;br&gt;
We mentioned above that keeping services cohesive to a specific task helps reduce complexity and increase agility by keeping services small and independently-deployable, with reduced risk of impacting other services.&lt;br&gt;
The potential risk of a change to a service impacting other services and the overall application is also highly-dependent on the degree of coupling that exists between services and resources. For example, the following illustrates a hypothetical implementation of an order service, which is responsible for first completing payment of an order, before sending instructions for its shipping fulfillment:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg28bmsm8866fpbj7n435.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg28bmsm8866fpbj7n435.png" alt="Tightly-coupled services" width="720" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Figure 6: Illustration showing coupling between services and the database&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Order Service here is dependent (tightly coupled) to Fulfillment Service; meaning that any changes that are made to Fulfillment Service have the potential to impact Order Service. Regression testing of both services should be considered for any such changes. Furthermore, a runtime failure of Fulfillment Service would also likely impact Order Service and possibly the user’s experience. Additional coupling is also illustrated by the fact that both services are sharing the same database. Any changes that are required to be made to the database for one service may also have consequences for the other service.&lt;br&gt;
In both these cases, clear communication and collaboration is needed between teams that manage both services whenever changes are required.&lt;/p&gt;

&lt;p&gt;On the other hand, an MSA-based alternative may look like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F52kpigq68xiibxcyivhx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F52kpigq68xiibxcyivhx.png" alt="Decoupled services" width="720" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Figure 7: Illustration showing decoupled services, as favoured for MSA&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Here, Order Service has been decoupled from Fullfilment Service, such that its order confirmation messages are sent en-route to Fulfillment Service via a publish-subscribe mechanism. The order service only needs to know how to send its messages to the message-based technology; for its order payment/confirmation use case it does not need to wait for a response from Fulfillment Service. The fulfillment service does not even need to be immediately available at the time messages are sent, as long as it is configured as a durable subscriber (for discussion in a future article).&lt;/p&gt;

&lt;p&gt;The refactored solution has also decoupled the data store, such that each service has its own database. This provides benefits not only in terms of allowing the service’s development team having full control of changes to its data store with greatly reduced risk of impacting other services, they can also select the database technology or vendor that is most suitable to them, providing polyglot persistent to the overall application. Similarly, teams have a choice of implementing their services in a programming language that is most suited to their skill sets and uses cases, provided that it is supported by their Cloud provider.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Services are stateless; to promote scalability and resilience.&lt;/strong&gt;&lt;br&gt;
This means that services do not need to remember information of previous messages received from a client; each client request is required to be handled as though it were received from a client for the first time. This feature enables automated horizontal scaling and failover of services, whereby new service instances can be created and destroyed as needed based on demand or failure, and any new instance should be able to handle requests from clients that were served by another instance before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges of Microservices
&lt;/h2&gt;

&lt;p&gt;Promoting change agility, scalability and resilience of applications are major benefits provided by MSA and Cloud Native Computing. However, many of its design principles introduce larger challenges that are not present with traditional monolithic solutions, and debates on many of these topics continue to rage, including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How big is a microservice, or rather how should the granularity of a microservice be decided?&lt;/li&gt;
&lt;li&gt;What are best practices for ensuring that microservices are effectively decoupled within a business domain, for optimal change agility and minimal maintenance in the future?&lt;/li&gt;
&lt;li&gt;How can shared databases be eliminated if direct communications or ‘chattiness’ between microservices should be avoided?&lt;/li&gt;
&lt;li&gt;How can work be roll-back if there is a partial failure; e.g. if a fulfillment operation cannot be completed, how can a user’s payment best be refunded?&lt;/li&gt;
&lt;li&gt;What are the best communication technologies and patterns for different use cases?&lt;/li&gt;
&lt;li&gt;How can global solutions best be implemented, to provide either regional disaster recovery/failover, or maximum performance for serving data consistent data to users no matter where they are?&lt;/li&gt;
&lt;li&gt;What prerequisites are required for embarking on an MSA implementation?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Indeed, catering for much of the above is a complex task and can require much initial investment, particularly for organisations that do not yet have mature DevOps, Agile teams and practices, or Site Reliability Engineering (SRE) capabilities. Change agility and scalability advantages of MSA are valuable for medium to large applications, however these may not be so important and only add a restrictive overhead to start-ups. Each of these however will be discussed further in the design of The Better Store.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coming next in &lt;a href="https://dev.to/aws-builders/building-the-better-store-an-agile-cloud-native-ecommerce-system-on-aws-part-2-defining-ddd-3hn5"&gt;Part Two: Using DDD Strategic Patterns to design The Better Store&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;“Domain Driven Design, Tackling Complexity in the Heart of Software”, Evans, E., &lt;em&gt;Addison-Wesley&lt;/em&gt; (2003)&lt;/li&gt;
&lt;li&gt;“Patterns, Principles, and Practices of Domain-Driven Design”, Millett &amp;amp; Tune, &lt;em&gt;Wiley &amp;amp; Sons&lt;/em&gt; (2015)&lt;/li&gt;
&lt;li&gt;“Design Patterns for Cloud Native Applications”, Indrasiri &amp;amp; Suhothayan, &lt;em&gt;O’Reilly&lt;/em&gt; (2021)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/#:~:text=The%20Onion%20Architecture%20picks%20up,direction%20is%20towards%20the%20centre" rel="noopener noreferrer"&gt;DDD, Hexagonal, Onion, Clean, CQRS, … How I put it all together&lt;/a&gt;, Graca, H, _web _(2017)&lt;/li&gt;
&lt;li&gt;“&lt;a href="https://aws.amazon.com/eventbridge/" rel="noopener noreferrer"&gt;Intro to Amazon EventBridge&lt;/a&gt;”, Beswick, J. (AWS), &lt;em&gt;web video&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Disclaimer:&lt;/em&gt; The views and opinions expressed in this article are those of the author only.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>microservices</category>
      <category>domaindrivendesign</category>
      <category>devops</category>
    </item>
    <item>
      <title>Building “The Better Store”  — Part 4: Implementing a Microservices Architecture with Cloud Native Patterns and AWS Services</title>
      <dc:creator>BrycePC</dc:creator>
      <pubDate>Wed, 18 Mar 2026 08:20:03 +0000</pubDate>
      <link>https://forem.com/aws-builders/building-the-better-store-part-4-implementing-a-microservices-architecture-with-cloud-native-585a</link>
      <guid>https://forem.com/aws-builders/building-the-better-store-part-4-implementing-a-microservices-architecture-with-cloud-native-585a</guid>
      <description>&lt;p&gt;The previous two articles of this series have provided introductions to Domain Driven Design principles, and how they may be used for defining an appropriate Microservice Architecture for our sample ‘The Better Store’ cloud-native ECommerce system.&lt;/p&gt;

&lt;p&gt;This article continues from the proposed DDD tactical design to formulate an implementation of our decomposed microservices, while describing and adopting popular cloud-native patterns to reap advantages that they provide.&lt;/p&gt;

&lt;p&gt;The focus here will be defining an implementation which has the following features scope, as defined in the previous Strategic Patterns section:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1: OrderPurchased&lt;/strong&gt;&lt;br&gt;
**When **I submit valid Card details&lt;br&gt;
**And **Payment is approved&lt;br&gt;
**Then **My order details with cart details will be stored in the Order Repository&lt;br&gt;
**And **The order details will be eventually persisted to the reporting database&lt;br&gt;
**And **An electronic Receipt will be emailed to me&lt;br&gt;
**And **A shipping order is sent for Fulfilment&lt;br&gt;
**And **I will be directed back to the store’s home page, with a notice confirming the order number.&lt;/p&gt;

&lt;p&gt;This logical flow is represented by the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0aim7p5wx86wp15eflt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0aim7p5wx86wp15eflt.png" alt=" " width="720" height="343"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;_Figure 1: Logical flow representing completion of the Order Purchased scenario.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;So now that we have identified the resources and required interactions between them for implementing flows, we need to determine exactly how to implement these in a way that provides optimal scalability, resilience and performance while considering cost with AWS as the platform of choice. Some questions that we can start asking ourselves while looking at the above high-level implementation designs are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is the best backend hosting technology to use for compute and data services that quickly scale on-demand, can be easily scaled
across regions for a global implementation, are resilient, provide cost optimization for both development and production environments in terms of compute and operational/maintenance costs.&lt;/li&gt;
&lt;li&gt;What are the best methods for enabling communications between services and data stores which provide scalability, resilience and
cost optimization that accommodate both development and production environments?&lt;/li&gt;
&lt;li&gt;What are the best methods for managing transactions and errors?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Microservice Architecture Patterns
&lt;/h2&gt;

&lt;p&gt;A number of patterns and best practices which help to answer these questions have been defined for microservice architectures; however Chris Richardson provides a great overview and illustration of these at his website &lt;a href="https://microservices.io/" rel="noopener noreferrer"&gt;https://microservices.io/&lt;/a&gt;. A cut-down version of this to illustrate those considered for The Better Store are shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvjjo4kq2hjyoosn7mu6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvjjo4kq2hjyoosn7mu6.png" alt=" " width="720" height="564"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 2. A summary of Microservice Patterns, highlighting key patterns for discussion with The Better Store in yellow.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;A description of those used and why by The Better Store are next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Patterns
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Decomposition&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;A. Decompose By Subdomain&lt;/strong&gt; describes how Domain Driven Design may be used to decompose a business’s domain into decoupled subdomains or &lt;em&gt;Bounded Contexts&lt;/em&gt;, each of which may be considered as a candidate for a microservice implementation. This topic has been the focus of our previous articles in this series.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B&lt;/strong&gt;. &lt;strong&gt;The Self Contained Service&lt;/strong&gt; pattern describes how services within an application are decoupled and can be updated and deployed with minimal risk of impacting other services. It also means services ideally should not synchronously call other services, or resources that they do not own such as shared databases; as in doing so can increases risk of release issues.&lt;br&gt;
Consider for example the following alternative implementations for order fulfilment requests upon receiving payment confirmation from a card merchant. The first illustrates tight coupling between Order and Fulfillment services and use of a shared database. In this topology, changes to either of the Order or Fulfilment services, or the shared database, risk impacting request processing, and consequently customers not receiving their items as expected if not appropriately remediated. Side effects can also include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Unexpected cloud usage charges; for example an issue causing a request to wait and consequently time out for a downstream service call will also impact its upstream requesting services.&lt;/li&gt;
&lt;li&gt;Larger system availability issues due to exhausted resources (for example AWS Lambda concurrency, or database connections) if requests are not able to complete.&lt;/li&gt;
&lt;li&gt;Potential issues in error handling when errors are returned to the payment and receipt system, which would need to be considered.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw08c2wno57tljvcaa7bo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw08c2wno57tljvcaa7bo.png" alt=" " width="720" height="369"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 3: Illustrating tight coupling between Order and Fulfilment services and use of a shared database.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;An alternative, decoupled and more resilient solution is shown below, noting that the client does not require data to be returned in a response; its calls may be made asynchronously. This solution offers the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Confirm Payment calls from the Payment system are placed on a queue with ‘Guaranteed At Least Once’ delivery; and a successful response is ALWAYS returned to it. The client just needs to know that the message has been delivered successfully.&lt;/li&gt;
&lt;li&gt;Both the Order and Fulfilment service receive requests asynchronously; it does not matter if they are not currently running, the messages will wait for them until they are next available.&lt;/li&gt;
&lt;li&gt;If either service fails in processing a request then processing may be configured to retry for a set number of times, after-which they may be placed on a “Dead Letter Queue” for appropriate error remediation.&lt;/li&gt;
&lt;li&gt;Both Order and Fulfilment services in this way are effectively decoupled, including having their own databases, such that an error introduced to one service should minimise impact for the other.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ts7r4jdqa38ike12608.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ts7r4jdqa38ike12608.png" alt=" " width="720" height="368"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 4: An alternative decoupled solution&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;A further note for the Better Store; this pattern has also been taken to describe how each service should own and define all of their application and infrastructure dependencies such as data storage, security resources etc; such that they can be deployed quickly and independently across environments. This includes for example having their own security roles, firewall definitions, databases (see below), SSL certificates and domain records defined; external/shared dependencies are kept to a minimum; such as the VPC in which they reside.&lt;br&gt;
In a future DevSecOps article, Infrastructure as Code (IaC) using AWS Cloudformation will be described, for the creation of fully-encapsulated Cloudformation stacks which may be used to deploy instances of independent microservices, including resources that they require.&lt;/p&gt;

&lt;p&gt;The example below illustrates the Order Cloudformation stack, which defines all of its required resources, and shared infrastructure stacks that it depends on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7sj6j61q2z1zjhcg210e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7sj6j61q2z1zjhcg210e.png" alt=" " width="720" height="595"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 5: The Order Service encapsulated for deployment as a self-contained AWS Cloudformation stack.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;To conclude this section on Self Contained Services, we can name some candidate AWS services for implementing the resources described:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Guaranteed at-least-once queueing: SQS&lt;/li&gt;
&lt;li&gt;Dead Letter Queues: SQS&lt;/li&gt;
&lt;li&gt;Asynchronous messaging between services (“Remote Procedure Invocation/RPC”): SNS, EventBridge, DynamoDB Streams&lt;/li&gt;
&lt;li&gt;Data stores: DynamoDB, RDS/RDS Aurora&lt;/li&gt;
&lt;li&gt;Self-contained IaC: Cloudformation&lt;/li&gt;
&lt;li&gt;Compute processing for processes of short duration, with fast scaling capabilities: Lambda&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Application Architecture&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;C. Monolithic&lt;/strong&gt;: where an application is built and deployed from a single source code repository. This may have advantages for smaller applications and startups while source code is new and small, while offering reduced complexity. However, its continued growth over time without checks can yield a system that is harder to change, scale and deploy, in a phenomenon coined the ‘Big Ball of Mud’ (Foote &amp;amp; Yoder).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;D. MSA&lt;/strong&gt;: as already discussed, is focused on change agility when decomposing applications into multiple services. It cares less about code reuse in contrast to earlier architectures; it may be that duplicate code can sometimes exist between services, but this does allow such code to be modified if required in an application, knowing that other applications will not be affected as a result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database Architecture&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;E. Database per Service&lt;/strong&gt;: is another decoupling approach recommended for microservices. Traditional relational databases can grow large to support data models shared by multiple services, while they also enforce relational constraints and atomic transactions across tables to maintain data integrity.&lt;br&gt;
Imposing the database/service pattern implies the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The database is split into objects specific for each microservice, which means breaking relational constraints and ACID transaction support otherwise provided.&lt;/li&gt;
&lt;li&gt;The application’s architecture needs to be refactored to cater for the loss of these constraints to preserve data integrity. Patterns that may assist include, ‘Saga’, and ‘Idempotent Consumer’, which are introduced below.
Advantages of the Database per Service again is change agility; any changes that may be required to a database should generally only impact its owning microservice. This greatly-reduces the risk of issues and the amount of regression testing that may otherwise be required when making changes. Furthermore, each service is free to use a database technology that is most suitable for their needs (aka polyglot persistence), for example:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;The Order microservice is expected to use AWS DynamoDB, a serverless NoSQL database which scales well for high-demand, and is capable of replicating data across regions for potential future global scalability of the application.&lt;/li&gt;
&lt;li&gt;The Reports microservice is expected to use AWS Aurora Serverless, to receive orders in batches, which supports complex relational queries using SQL to provide overnight reports. Its serverless nature is expected to provide cost optimisation for its low intended traffic, while any cold-starts in its activity will not impact users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;F. Saga&lt;/strong&gt; is a pattern that addresses the problem of how to manage business transactions that span multiple services and/or databases, for example when implementing the Database per Service pattern described above, and including a distributed transaction e.g. via a 2-phase-commit is either too complex or not possible for error handling. It describes a process whereby such transactions are implemented as a sequence of partial transactions against each of the participant databases. If any single step of the transaction fails, then previous changes are to be rolled-back by running copensating transactions in the reverse order.&lt;/p&gt;

&lt;p&gt;An example of a choreography-based saga including compensating-rollback of transactions is given below (where system behaviour is asynchronously event-driven):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24noonisczumm3nhekl0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24noonisczumm3nhekl0.png" alt=" " width="720" height="401"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 6: Saga pattern illustrating compensating actions to roll-back a transaction.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Infrastructure Patterns
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Communication Patterns&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;G: Remote Procedure Invocations (RPI)&lt;/strong&gt;: refers to the use of standard synchronous request/reply protocols for inter-service communications, for example via REST, gRPC. These have advantages over Remote Procedure Calls (RPC’s) between services, which are dependent on a specific programming language being used between client and server; such as an SDK call between a NodeJS application and AWS’s NodeJS SDK.&lt;/p&gt;

&lt;p&gt;Inter-service communications using standard protocols are sometimes necessary for processing of requests, and RPI’s use of the request/reply pattern allows this to be achieved simply. The pattern however does result in tight-coupling between services involved, as discussed for the Self-Contained Service pattern above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;_Candidate AWS services:&lt;/strong&gt; API Gateway, AppSync_&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;H. Messaging:&lt;/strong&gt; refers to the use of asynchronous message channels for inter-service communications, in-contrast to synchronous Remote Procedure Invocations.&lt;br&gt;
As previously described in the Self-Contained Service pattern described above, use of asynchronous messaging is aimed at decoupling services and increasing overall system availability, such that a change to one service should generally be seamless to services that communicate with it.&lt;/p&gt;

&lt;p&gt;The pattern also includes different types of communication; for example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Notification; a sender sends a message to a recipient, and does not expect a reply.&lt;/li&gt;
&lt;li&gt;Request/asynchronous response — where the recipient replies eventually. The sender does not block waiting.&lt;/li&gt;
&lt;li&gt;Publish/subscribe — a service sends messages to 0, 1 or more subscribers. These may also be ‘durable consumers’; to guarantee they will receive messages eventually if they are not currently running.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;_Candidate AWS services:&lt;/strong&gt; SQS, EventBridge, SNS._&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I. Idempotent Consumer&lt;/strong&gt;: is a key pattern requires full consideration when implementing microservices; it means that a service must be able to handle requests if received more than once with no side effect; i.e, the outcome of processing a request repeatedly must be the same as if only processed once.&lt;/p&gt;

&lt;p&gt;The reason why this pattern is so important is that a number of AWS services guarantee ‘At Least Once’ message delivery to consumers; i.e. no messages will be lost, but duplicates may be received and the consuming service must be able to deal with these.&lt;/p&gt;

&lt;p&gt;Such examples include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SQS may redeliver a message to a consumer if previously consumed but has not been acknowledged as processed, before its Delivery Timeout period has elapsed.&lt;/li&gt;
&lt;li&gt;Asynchronous requests may be automatically retried by some AWS services on encountering an error. For example, if an error is thrown from a lambda function, the lambda function will automatically retry processing 2 further times in case it was transient, and if still not successful it will place the request on a Dead Letter Queue if configured.&lt;/li&gt;
&lt;li&gt;Failed message deliveries from EventBridge, SQS and SNS all may result in messages being retried, and being delivered to a Dead Letter Queue if a threshold has been exceeded.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The design of idempotent consumers does also have benefits for error handling; for example; if a single request contains 100 records in which only 1 fails; the request can be safely retried following correction of the error for the single record; resending of the other 99 records will not result in any change to the system.&lt;/p&gt;

&lt;p&gt;Methods for implementing this pattern may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensuring that every request has a unique identifier and recording receipt of these in a data store when messages are received and processed. Any subsequent receipts of the messages may be ignored.&lt;/li&gt;
&lt;li&gt;For some applications designing requests to contain all state of a request to be processed, such that performing an update in the datastore for the record will not result in any change.&lt;/li&gt;
&lt;li&gt;Ensuring that requests have a timestamp included from the receiving system, and only processing a record if this is newer than the timestamp last received by the consumer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;J. Api Gateway:&lt;/strong&gt; is often implemented in front of a service to act as a single entry point for its clients, to provide the following capabilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They define a service’s Published Language (refer to DDD Strategic Patterns) / interface contract via an open-standard specification such as Swagger or OpenAPI, for purposes of providing a shared understanding of required input data for requests and the expected output, between developers and its consumers. It is intended that these specifications provide all the information that its consumers require, the inner workings of the service do not need to be known.&lt;/li&gt;
&lt;li&gt;They may serve simply as a proxy layer to underlying services, while offering additional capabilities such as authentication, authorisation, request throttling (e.g. to protect the system from unexpected surges in traffic), WAF and transport-based encryption.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Candidate AWS services:&lt;/strong&gt; Api Gateway, AppSync (supporting GraphQL)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observability&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;K. Metrics:&lt;/strong&gt; provide a continuous stream of data points over time as a measure of the performance and health of an application and its resources, for monitoring and potential remediation. Example metrics include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Counts of consumer requests and errors over time&lt;/li&gt;
&lt;li&gt;Average, maximum and minimum request durations for request processing (latency) over time&lt;/li&gt;
&lt;li&gt;CPU (%) and system memory (e.g. MB) used over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Candidate AWS services:&lt;/strong&gt; Cloudwatch Metrics&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L. Log Aggregation:&lt;/strong&gt; refers to a centralized logging service that aggregates logs from multiple service instances, for easy accessibility and analysis.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa8wxw7g2g38ye4o02ue5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa8wxw7g2g38ye4o02ue5.png" alt=" " width="720" height="466"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 7: Screenshot of AWS Cloudwatch Insights, which allows log groups to be queried using a SQL-like syntax for fast analysis and troubleshooting.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Candidate AWS services:&lt;/strong&gt; Cloudwatch Logs, Cloudwatch Insights, OpenSearch&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;M. Distributed Tracing:&lt;/strong&gt; provides the ability to determine how a single request may traverse across multiple services for its processing within a distributed system, which is made possible by their allocation of a unique trace id when first received.&lt;br&gt;
Distributed tracing provides the following benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It enables developers to understand the flow of processing events for a request.&lt;/li&gt;
&lt;li&gt;Can help identify performance bottlenecks at different processing points in the system.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fikc3admkjfuen3gqbqwy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fikc3admkjfuen3gqbqwy.png" alt=" " width="720" height="553"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 8: Screenshot of an AWS X-ray trace for processing of a single request.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Candidate AWS services:&lt;/strong&gt; XRay, Open Telemetry&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;N. Dashboards:&lt;/strong&gt; provide a graphical collection of metrics for a defined portion of the source to give a holistic view of its behaviour.&lt;/p&gt;

&lt;p&gt;The following provides an example specific to the Order service, providing a view of resources that it contains:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feadrjjbhxwv754xx987x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feadrjjbhxwv754xx987x.png" alt=" " width="720" height="471"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 9: Cloudwatch Dashboard constructed specifically for monitoring resources belonging to the Order service.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Candidate AWS services:&lt;/strong&gt; Cloudwatch Dashboards, OpenSearch (Kibana), Grafana&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O. Alarms:&lt;/strong&gt; These may be used to provide notifications to IT Staff in cases where manual intervention is required when certain system metric thresholds being exceeded.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Request volumes are higher than the system’s capacity, to cause throttling of some requests (throttling metric &amp;gt; 0).&lt;/li&gt;
&lt;li&gt;Asynchronous requests have failed processing following &lt;em&gt;x&lt;/em&gt; amount of retries, and have been placed in the configured Dead Letter Queue (which has an alarm threshold &amp;gt; 0 for a defined period).&lt;/li&gt;
&lt;li&gt;Synchronous requests to a lambda are failing; where the lambda Error metric threshold is &amp;gt; 0.&lt;/li&gt;
&lt;li&gt;Relational database CPU is &amp;gt; 90% for a defined period; vertical scaling may need to be considered.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Candidate AWS services:&lt;/strong&gt; Cloudwatch Alarms, OpenSearch (Kibana), Grafana, SNS (notifications)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure Patterns
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;P. Services/Host or VM:&lt;/strong&gt; Involves deploying a number of services or potentially an entire system on a single host.&lt;br&gt;
This may initially provide advantages of simplicity and efficient resource utilization in contrast to a Service/VM pattern, but it also has the following disadvantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Difficulty in isolating resource usage between services and reduced availability because of this; an errant service, or host issue will impact multiple services.&lt;/li&gt;
&lt;li&gt;Potential difficulty/less efficiency in being able to horizontally-scale a single small service, when larger more resource-consuming resources also need to be included.&lt;/li&gt;
&lt;li&gt;Maintenance of the underlying Operating System is typically the responsibility of the cloud account holder, including OS updates and security patching.&lt;/li&gt;
&lt;li&gt;Horizontal scaling involves instantiating new VM’s, which due to the required startup of their OS and other underlying services can be slow.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Candidate AWS services:&lt;/strong&gt; EC2 (shared or dedicated hosting)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q. Service/Host or VM:&lt;/strong&gt; Involves deploying single services into their own dedicated host VM’s. This provides advantages over Services/Host or VM, in that services are isolated from each other, at the cost of having to maintain and pay for additional hosts or VM’s.&lt;br&gt;
Horizontal scaling of entire VM’s is slow, but potentially faster than if hosting multiple services/VM.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Candidate AWS services:&lt;/strong&gt; EC2 (shared or dedicated hosting), Beanstalk&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;R. Service/Container:&lt;/strong&gt; Involves packaging services as docker images, and deploying them into isolated docker containers.&lt;br&gt;
Benefits of container vs VM deployments include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Horizontal scaling of docker instances is much faster in contrast to starting new VM’s and their underlying OS’s.&lt;/li&gt;
&lt;li&gt;The container image also encapsulates the runtime that the service requires; which provides portability with consistent for deployment of services into different environments.
Note unless serverless options are used, maintenance of their underlying host VM including OS is still required.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Candidate AWS services:&lt;/strong&gt; Beanstalk, ECS, Kubernetes, App Runner&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S. Serverless:&lt;/strong&gt; Refers to the deployment of services to compute platforms which hide their underlying server details; their cloud provider instead assumes responsibility for managing underlying hosts, their associated infrastructure, and OS patching.&lt;br&gt;
Typically the implementor needs to only provide the amount of memory (GB) and/or the number of virtual CPU’s that are to be allocated to a service executable.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Candidate AWS services:&lt;/strong&gt; ECS (Fargate), Kubernetes (Fargate), Lambda.&lt;br&gt;
Of note, while the first 3 services above provide container-based hosting of services, Lambda provides Function as a Service (FaaS) capabilities; where each implementation provides a single compute function only, which are designed to run transactions of short duration but which can scale very quickly based on consumer demand.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next, we will look at how some of these patterns may be used for our chosen Use Cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;On consideration of the microservice patterns and candidate AWS services for their implementation, we conclude here in defining an implementation view, as illustrated below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqa9oxbnuxrubrtqnkjam.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqa9oxbnuxrubrtqnkjam.png" alt=" " width="720" height="391"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 10: Implementation view for Order Purchased scenario&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Decisions made for this architecture include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Order, fulfilment and reporting services will be implemented as separate decoupled AWS microservices; each of which will be defined as separate AWS Cloudformation stack instances (tbs-app-order-prod, tbs-app-reports-prod, tbs-app-fulfilment-prod) that are defined in their own GitHub repositories, matching the stack names. This aligns well to the Decompose by Subdomain and Self Contained Service patterns.&lt;/li&gt;
&lt;li&gt;The Database per Service pattern will be used such that tbs-app-order and tbs-app-reports services have their own database that is best-suited for their use-case.
The tbs-app-order service will implement DynamoDB as a highly-scalable and potentially global database that can accommodate high traffic volumes, where structured data and complex query capabilities are not required. The tbs-app-reports database will implement AWS Aurora Serverless v2, to subscribe to batched order confirmation updates, and allow these to be stored in a structured manner to allow complex queries for monthly reporting. As its traffic volumes are expected to be low and immediate responses are not required by clients (cold-starts of the dabase can be accommodated), scaling down to 0 CPU will be used for cost optimization. Finally, it is expected that running of queries can target the database’s read-only endpoint, to not impact writing to the database (though again this is probably not required for its expected traffic).&lt;/li&gt;
&lt;li&gt;Inter-service communications will be asynchronous using messaging where possible, using primarily AWS EventBridge. AWS EventBridge offers similar capabilities to SNS for implementing the publish/subscribe pattern; it is a little bit slower which is not deemed so important to us for asynchronous communications, as it has advantages over SNS including more integration options, content-based subscriptions (i.e. subscribers can select to receive requests based on their content), and event storing (although this and event sourcing is not considered further here; it has its own complexities). SQS is used for guaranteed message delivery, where it is integrated with API Gateway to receive payment confirmation messages from the payment system (Stripe). In this way our webhook that we configure in Stripe to invoke has very high availability; success responses will always be returned to Stripe as messages are placed on the queue for processing.&lt;/li&gt;
&lt;li&gt;Aysynchronous services will implement the Idempotent Consumer pattern, to support Guaranteed At Least Once delivery properties of SQS and EventBridge, and the automated retry ( 2 times) behaviour of asynchronously-triggered AWS Lambda functions if they throw an error. Dead Letter Queues (SQS) will be configured for SQS queues, EventBridge and Lambda functions where appropriate, to ensure that for Lambdas; errored requests are not lost, and other services such as EventBridge, that retries do not continue forever!&lt;/li&gt;
&lt;li&gt;Synchronous messaging via Remote Procedure Invocation will be implemented as RESTful API’s using API Gateway. Examples of its use include requests from the client website to retrieve and post data, where the client is dependent on information returned in the response.&lt;/li&gt;
&lt;li&gt;AWS Cloudwatch will be used for monitoring metrics and logs of services and their associated resources, and providing monitoring dashboards and alarm capabilities. AWS X-ray will be used for distributed tracing of requests received.
Note other services such as OpenSearch and Managed Grafana are also available and may provide greater capabilities; Cloudwach has been chosen due to its simplicity for implementation while providing sufficient capabilities at low cost for our needs.&lt;/li&gt;
&lt;li&gt;Serverless resources will be used where possible for our implementations, for reduced maintentance that would other be required for managing and patching servers, its generally-faster horizontal scalability and use of the Pay As You Go model which is generally favourable, especially for non-production systems! AWS Lambda is currently used to provide all compute functionality for The Better Store, while all of its request processing workloads are small and of very short duration (i.e. &amp;lt; 10 seconds, where AWS Lambda offers request processing for durations of up to 15 minutes).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To conclude, sample code (as Cloudformation templates and NodeJS implementations) for the services described here and other supporting stacks may be viewed on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coming soon in Part Five:&lt;/strong&gt; Use of DevSecOps; specifically AWS Cloudformation for defining both applications and infrastructure as code, for fully-automated deployments.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://microservices.io/patterns/index.html" rel="noopener noreferrer"&gt;“A pattern language for microservices”&lt;/a&gt;, Richardson, Chris. _web _(2023)&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.laputan.org/mud/" rel="noopener noreferrer"&gt;“Big Ball of Mud”&lt;/a&gt;, Foote &amp;amp; Yoder (University of Illinois), _web _(1999)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.thebetterstore.net/" rel="noopener noreferrer"&gt;“The Better Store Documentation”&lt;/a&gt;, Cummock, B. _web _(2025)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/thebetterstore" rel="noopener noreferrer"&gt;“The Better Store Github Repository”&lt;/a&gt;, Cummock, B. _web _(2025)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Disclaimer&lt;/em&gt;: The views and opinions expressed in this article are those of the author only.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>microservices</category>
      <category>ddd</category>
    </item>
    <item>
      <title>Building “The Better Store” an agile cloud-native ecommerce system on AWS — Part 3: DDD Tactical Patterns and App Architecture</title>
      <dc:creator>BrycePC</dc:creator>
      <pubDate>Wed, 18 Mar 2026 07:32:58 +0000</pubDate>
      <link>https://forem.com/aws-builders/building-the-better-store-an-agile-cloud-native-ecommerce-system-on-aws-part-3-ddd-tactical-92b</link>
      <guid>https://forem.com/aws-builders/building-the-better-store-an-agile-cloud-native-ecommerce-system-on-aws-part-3-ddd-tactical-92b</guid>
      <description>&lt;p&gt;&lt;a href="https://medium.com/@BrycePC/building-the-better-store-an-agile-cloud-native-ecommerce-system-on-aws-part-2-defining-ddd-f4523380bb1" rel="noopener noreferrer"&gt;Part Two&lt;/a&gt; of Building the Better Store described the use of DDD Strategic Patterns as tools for tackling complex requirements for a business system, by decomposing the problem domain to produce a high level design composed of decoupled ‘Bounded Contexts’; each of which may be considered as an initial blueprint for a Microservice.&lt;/p&gt;

&lt;p&gt;This article continues from the conceptual view produced in part two, and becomes more technically-focused on implementation details using &lt;em&gt;DDD Tactical Patterns&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing DDD Tactical Patterns
&lt;/h2&gt;

&lt;p&gt;DDD tactical patterns, also known as ‘model building blocks’, are used to help define static models for complex bounded contexts.&lt;br&gt;
The main patterns and their relationships are illustrated as:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrzuvoawm3oav1ng3kky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrzuvoawm3oav1ng3kky.png" alt=" " width="720" height="551"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 1: DDD Tactical Patterns&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;where each of the patterns may be described as below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgjlf0ifmkvxw7rn7s58k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgjlf0ifmkvxw7rn7s58k.png" alt=" " width="720" height="474"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 2: Table defining main tactical patterns, for designing a Bounded Context.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Order Bounded Context
&lt;/h2&gt;

&lt;p&gt;The Order bounded context was first introduced in my &lt;a href="https://medium.com/@BrycePC/building-the-better-store-an-agile-cloud-native-ecommerce-system-on-aws-part-2-defining-ddd-f4523380bb1" rel="noopener noreferrer"&gt;Part 2: Defining Defining DDD Strategic Patterns&lt;/a&gt; article, as representing a core subdomain responsible for managing orders and payments within The Better Store. Its DDD strategic design included the following:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A. BDD Features&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;PurchaseProductsInCartFeature&lt;/strong&gt;; order-related scenarios include:
&lt;strong&gt;&lt;em&gt;@ConfirmOrder&lt;/em&gt;&lt;/strong&gt;; an Order consists of Products and their quantities in the cart, the Customer and associated email address, delivery address and shipping cost (of order contains physical products). The customer is directed to the payment system for completion here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ManageOrderFeature&lt;/strong&gt;; order management scenarios including:
&lt;strong&gt;&lt;em&gt;@ViewOrder&lt;/em&gt;&lt;/strong&gt;; allows details of a previously-created order to be retrieved from the system.
&lt;strong&gt;&lt;em&gt;@ViewOrderHistory&lt;/em&gt;&lt;/strong&gt;; allows a list of previous orders created for a customer over the last 6 months to be retrieved.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;B. Class Responsibility Collaboration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcqmxltyd4j9idch8rq14.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcqmxltyd4j9idch8rq14.png" alt=" " width="720" height="581"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 3: Collaboration Responsibility Card for the Order subdomain, illustrating relationships with other subdomains&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Combining these strategic design outputs with the described tactical patterns, an initial draft high-level class diagram may be constructed as below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9tdvgdbcuoqgxsk7cnis.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9tdvgdbcuoqgxsk7cnis.png" alt=" " width="720" height="476"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 4: High-level class diagram representing a static view of the Order bounded context, for a potential microservice implementation&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Note that while this provides us with a good start for an object-oriented design of how we may wish to implement an Order microservice using an object-oriented language , we need to at this point consider an appropriate Application Architecture for structuring the service, using layering principles to help ensure the application can be easily extended and maintained into the future, while avoiding the potential &lt;em&gt;Big Ball of Mud&lt;/em&gt;[7] anti-pattern! For this we will be using the Onion Architecture, as described next.&lt;/p&gt;




&lt;h2&gt;
  
  
  Application Architecture with Layering; Introducing Onion Architecture!
&lt;/h2&gt;

&lt;p&gt;A &lt;em&gt;layered&lt;/em&gt; application architecture is a standard technique used by software developers and application architects to structure application source code into abstract layers (or tiers); for example by splitting code into separate subdirectories, modules and/or namespaces within the application’s code repository based on their general concerns; such as &lt;em&gt;presentation&lt;/em&gt;, &lt;em&gt;business domain logic&lt;/em&gt;, and &lt;em&gt;data access&lt;/em&gt;. An example of this topology is illustrated below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frarunxl4pmdrr8h314xt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frarunxl4pmdrr8h314xt.png" alt=" " width="720" height="391"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 5. Illustrating implementation and deployment of an n-tier application&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;This also promotes a top-down dependency model, whereby higher layers can only communicate with the layer above them; for example logic within the presentation layer cannot directly obtain data from the database via the data access layer; such queries must be via calls to the business layer.&lt;/p&gt;

&lt;p&gt;Advantages of a layered architecture for realizing Separation of Concerns include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code complexity is reduced as is it organised within its area of concern. This allows application logic to be easier found and changed, with reduced risk of impacting other areas of the application. For example, making changes to the user interface may be performed with little or no change or regression testing being required for other application layers.&lt;/li&gt;
&lt;li&gt;Such an architecture may even render it possible for an application’s entire user interface, or underlying database product to be replaced within an application, with little or no change being required to its business logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, while the decomposition of an application into layers helps reduce its complexity, it operates at a high-level and does not necessarily include best practices for structuring code within the layers, or to align with artefact types modeled using Domain Driven Design. Each layer within a larger application can quickly become unwieldly and at risk of the ‘Big Ball of Mud’ as an application becomes larger if left unchecked without adoption of further decomposition and decoupling best practices and standards, such as &lt;em&gt;Inversion of Control&lt;/em&gt; and _SOLID _by the development team. The Onion Architecture helps with the realization of these.&lt;/p&gt;




&lt;p&gt;The &lt;strong&gt;Onion Architecture&lt;/strong&gt; was first defined by Jeffrey Palermo [8] as a layered application dependency model, whereby outer layer components may be dependent on any of its lower-layer components, as illustrated in figure 6 below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0pxfqcwbpy1qfv37k4v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0pxfqcwbpy1qfv37k4v.png" alt=" " width="720" height="510"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 6: Layered application design with Onion Architecture&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;The architecture however exhibits the following differences to the n-tier layered architecture as described earlier:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It promotes &lt;em&gt;Inversion of Control (IoC)&lt;/em&gt; to provide loose or interchangeable coupling of components. In this respect, each layer/circle within its model encapsulates internal implementation details and exposes an interface for outer layers to consume.&lt;/li&gt;
&lt;li&gt;The inner components are comprised of domain entities and services as defined by our DDD tactical patterns to provide core business functionality. Also included are abstract interfaces, for example data access methods, as illustrated in the Application Core in figure 7. Their concrete methods however are implemented at the outermost infrastructure layer, using IoC. This is because the technology implemented for a database, or other external dependency such as HTTP Rest endpoints or technology-specific adapter should be agnostic to business domain components, and be able to be substituted with another product if needed. They may also be substituted with a mock component for automated testing purposes.&lt;/li&gt;
&lt;li&gt;Application services are often implemented to provide an additional decoupled layer above domain services, for example to:&lt;/li&gt;
&lt;li&gt;Serve as a proxy to requests to domain services, but with inclusion of additional functionality such as authentication/authorisation, or request/response object transformation, to satisfy system requirements. - Orchestrate calls to underlying domain services to meet specific use cases.&lt;/li&gt;
&lt;li&gt;The Infrastructure layer typically includes:

&lt;ul&gt;
&lt;li&gt;API servers such as REST API gateways, which manage and propagate externally-received requests to underlying application and domain service components.&lt;/li&gt;
&lt;li&gt;Presentation components, such as a web user interface, which may be dependent on calls to application and domain services (as well as API’s as described above).&lt;/li&gt;
&lt;li&gt;Repository components for accessing data stores; e.g. relational or NoSQL databases.&lt;/li&gt;
&lt;li&gt;Adapters for accessing external dependencies; for example a REST HTTP client or AWS SQS client, both with bespoke security, logging and error handling requirements included.&lt;/li&gt;
&lt;li&gt;Automated unit or integration tests. These can include dependency injection configurations which mock external dependencies, to provide fast and consistent test results within an isolated environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Details of IoC are out-of-scope of this article; further information of this and related SOLID principles which relate well to the technical implementation of The Better Store is well-described in R. Jansen’s web article: &lt;a href="https://dev.to/remojansen/implementing-the-onion-architecture-in-nodejs-with-typescript-and-inversifyjs-10ad?utm_source=dormosheio&amp;amp;utm_campaign=dormosheio"&gt;Implementing SOLID and the onion architecture in Node.js with TypeScript and InversifyJS&lt;/a&gt; [10]. The following class diagrams and sample code however provide an example of how InversifyJS may be used within our NodeJS+Typescript OrderService implementation to define interface bindings to concrete RestApi client class for our main application, and the same interface bindings to a mock Payment API client class for a corresponding automated test application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3snm1aw1e1kaxw32eep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3snm1aw1e1kaxw32eep.png" alt=" " width="720" height="1578"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;Figure 7: Illustrating the use of a REST API Client adapter interface within the Application Core layer, and a choice of decoupled concrete classes within the Infrastructure layer which may be configured for the application via an IoC. For example, RestApiClient may be configured to be used by the application for production deployment; whereas a separate test build may be configured to use MockPaymentsApiClient to enable automated testing of the application independent of the Payments solution.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Using the Onion Architecture constructs described in the previous section, our Order microservice application architecture may be further refined as the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fikfofc4a1mwbr46clm8x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fikfofc4a1mwbr46clm8x.png" alt=" " width="720" height="637"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 8: A high-level class diagram describing a potential Order microservice using the Onion Architecture, with swimlanes denoting its layers.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;A complete AWS Serverless implementation of the architecture using Node.js with Typescript and Inversify for Dependency Injection will be described in the future Part Six article, however the following screenshot provides a taster of what we expect to come, in terms of its code scaffolding. Its code is available to view at: &lt;a href="https://github.com/TheBetterStore/tbs-app-order" rel="noopener noreferrer"&gt;https://github.com/TheBetterStore/tbs-app-order&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk4g2y8l0x2bpmekphpk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk4g2y8l0x2bpmekphpk.png" alt=" " width="720" height="424"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 9: Screenshot illustrating NodeJS+Typescript source code scaffolding to realise the described application architecture.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coming next in Part Four: Building the Microservices Architecture with Cloud Native Design Patterns and AWS Services.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;“Domain Driven Design, Tackling Complexity in the Heart of Software”, Evans, E., Addison-Wesley (2003)&lt;/li&gt;
&lt;li&gt;“Patterns, Principles, and Practices of Domain-Driven Design”, Millett &amp;amp; Tune, Wiley &amp;amp; Sons (2015)&lt;/li&gt;
&lt;li&gt;“Practical Event-Driven Microservices Architecture”, Rocha, H., Apress (2022)&lt;/li&gt;
&lt;li&gt;“Building Microservices”, Newman, S., O’Reilly (2015)&lt;/li&gt;
&lt;li&gt;“Domain Driven Design &amp;amp; Microservices for Architects”, Sakhuja, R., Udemy (2021)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/en-us/previous-versions/msp-n-p/ee658117(v=pandp.10)?redirectedfrom=MSDN#DomainModelStyle" rel="noopener noreferrer"&gt;“Microsoft Application Architecture Guide, 2nd ed”&lt;/a&gt;, Microsoft, _web _(2013)&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.laputan.org/mud/" rel="noopener noreferrer"&gt;“Big Ball of Mud”&lt;/a&gt;, Foote &amp;amp; Yoder (University of Illinois), _web _(1999)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/" rel="noopener noreferrer"&gt;“The Onion Architecture: part 1”&lt;/a&gt;, Palermo, J., _web _(2008)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/#:~:text=The%20Onion%20Architecture%20picks%20up,direction%20is%20towards%20the%20centre" rel="noopener noreferrer"&gt;“DDD, Hexagonal, Onion, Clean, CQRS, … How I put it all together”&lt;/a&gt;, Graca, H, _web _(2017)&lt;/li&gt;
&lt;li&gt;“Implementing SOLID and the onion architecture in Node.js with TypeScript and InversifyJS”, Jansen, R., web (2018)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/expedia-group-tech/onion-architecture-deed8a554423" rel="noopener noreferrer"&gt;“Onion Architecture Let’s slice it like a Pro”&lt;/a&gt;, Kapoor, R., web(2022)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Disclaimer&lt;/em&gt;: The views and opinions expressed in this article are those of the author only.&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>aws</category>
      <category>ddd</category>
      <category>cloudnative</category>
    </item>
    <item>
      <title>Achieving organisation-scoped AWS Config compliance using Cloudformation Lambda Hooks</title>
      <dc:creator>BrycePC</dc:creator>
      <pubDate>Tue, 17 Mar 2026 08:24:49 +0000</pubDate>
      <link>https://forem.com/aws-builders/achieving-organisation-scoped-aws-config-compliance-using-cloudformation-lambda-hooks-afo</link>
      <guid>https://forem.com/aws-builders/achieving-organisation-scoped-aws-config-compliance-using-cloudformation-lambda-hooks-afo</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;AWS Cloudformation Hooks is an existing AWS feature that helps ensure compliance of AWS resources when being created or updated in accounts as Infrastructure as Code (IaC) via Cloudformation or CDK , against an organization’s defined standards. Examples of such checks can include ensuring that RDS, S3 or other data storage resources are configured with at-rest encryption, that log groups have retention policies, EC2 instances are not publicly-exposed etc. These rules typically strongly-align with and promote AWS Well-Architected best practices.&lt;/p&gt;

&lt;p&gt;AWS first introduced the ability to create custom Cloudformation hooks in 2022, using Java or Python code to allow organizations to also define their own rules. Implementation required the following steps:&lt;br&gt;
i. Initialising an AWS Cloudformation Hooks project using the Cloudformation CLI.&lt;br&gt;
ii. Implementing the hook’s handler logic to evaluate compliance of resources included within Cloudformation stacks.&lt;br&gt;
iii. Packaging and registering the hook in Cloudformation.&lt;/p&gt;

&lt;p&gt;The creation of these hook projects unfortunately are often not simple, where much boilerplate code is often required, and packaging and registering of hooks also requires further effort.&lt;/p&gt;

&lt;p&gt;AWS however have recently (since November 2024) made available 2 new hook types which make these configurations easier, being:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Guard Hooks&lt;/strong&gt;: These allow the specification of &lt;a href="https://docs.aws.amazon.com/cfn-guard/latest/ug/writing-rules.html" rel="noopener noreferrer"&gt;AWS Cloudformation Guard rules&lt;/a&gt; to set requirements for resources within Cloudformation templates.
&lt;strong&gt;&lt;em&gt;Pros&lt;/em&gt;&lt;/strong&gt;: Uses an open-source Domain Specific Language (DSL) to define rules for simplicity, rather than needing to compile and package code to implement. They have no associated execution charges.
&lt;strong&gt;&lt;em&gt;Cons&lt;/em&gt;&lt;/strong&gt;: Its own DSL may lack flexibility of logic, and require a learning curve in-contrast to a more-general language where in-house skills may already exist&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Hooks&lt;/strong&gt;: Custom logic for interrogating resources within a Cloudformation changeset or stack can also be implemented as lambda functions. Lambda hooks may then simply be configured to reference the lambda implementations.
&lt;strong&gt;&lt;em&gt;Pros&lt;/em&gt;&lt;/strong&gt;: Provide further flexibility for implementing compliance logic in contrast to Guard Hooks, while also being simpler to implement and configure than Custom Hooks. Rule behaviour can be easily amended within the lambda function, without requiring reconfiguration of the hook in Cloudformation. Hook rules may also be implemented in any language that lambda supports.
&lt;strong&gt;&lt;em&gt;Cons&lt;/em&gt;&lt;/strong&gt;: AWS does not charge for the hook, but standard lambda execution changes do apply.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While considering how Cloudformation hooks may assist with ensuring standards compliance with the previously-introduced “The Better Store” sample project, I have chosen to explore lambda Cloudformation hooks further here, and how they may be integrated with AWS Organizations/Stack Sets to automatically validate Cloudformation templates for all AWS accounts/regions within defined Organization Units (OU’s). This setup and its results are described next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution Prerequisites&lt;/strong&gt;&lt;br&gt;
The following are required for implementing the organisation-level hooks for the example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AWS Organizations has been configured, with the following being set on the Organizations Master Account:

&lt;ul&gt;
&lt;li&gt;Within AWS Organizations/Services section of the AWS web UI, the service “AWS Account Management” is enabled.&lt;/li&gt;
&lt;li&gt;A separate ‘Tools’ account has been provisioned within the Organization, which will be used for performing organization-scoped Cloudformation deployments via StackSets.&lt;/li&gt;
&lt;li&gt;The “Tools” account has been configured as a ‘Delegated Administrator’ for Cloudformation stacksets within the AWS Organizations master account (via the Cloudformation Stacksets web UI).&lt;/li&gt;
&lt;li&gt;Target accounts which are to be automatically configured with Cloudformation hooks are provisioned within target Organization Units in AWS Organizations (e.g. ‘non-prod’ for all non-production workload accounts).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A deployment bucket is created within the tools account, to store built lambda artefacts and Cloudformation templates which deploy them. A bucket policy is defined which provides GetObject access to its objects, to all accounts defined within the AWS Organization.&lt;/li&gt;
&lt;li&gt;A deployment user is configured in the “Tools” account, which has the following permissions:

&lt;ul&gt;
&lt;li&gt;Write access to the above S3 deployment bucket.&lt;/li&gt;
&lt;li&gt;Cloudformation permissions&lt;/li&gt;
&lt;li&gt;AWS Organizations query permissions
A user access key is also securely created and used for this to perform deployments, and an AWS user profile ‘thebetterstore-tools’ is configured to use these. &lt;em&gt;NB it is expected a productionized process would perform deployments within a DevOps pipeline, and that IAM roles may instead be used, which are preferred over user access keys.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The AWS CLI is installed (examples assume we are using a Linux environment, though Windows can also be used with minor tweaks).
N5. odeJS v20+ (the example will be implemented in Javascript, to target the NodeJS 20.x runtime)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;The following illustrations describe my target deployment architecture for lambda Cloudformation hooks in an organization, and the composition of components used:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta0ejvbmf2biypqp7zps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta0ejvbmf2biypqp7zps.png" alt=" " width="720" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;&lt;strong&gt;Figure 1.&lt;/strong&gt; High-level Lambda Hook Deployment via AWS Organizations / StackSets. Deployment of organization-specific Cloudformation StackSets has been delegated to a separate Tools account, which is also responsible for building the lambda hooks project and publishing these to the deployment bucket. A Hooks stackset automatically-deploys changes to the lambda hooks stack (tbs-devops-cfnhooks) across existing and/or new AWS accounts within specified target OU’s.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70lkci7o0zww78i0vz9n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70lkci7o0zww78i0vz9n.png" alt=" " width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;&lt;strong&gt;Figure 2&lt;/strong&gt;: Illustrating the composition of deployed CfnHooks stacks, hook configuration scope, and the relationship of the hooks to target resources&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;where main components are implemented with the following solution scaffolding:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;bin&lt;/strong&gt;&lt;br&gt;
— deploy-setup.sh&lt;br&gt;
— deploy-stackset.sh&lt;br&gt;
&lt;strong&gt;deploy-setup/&lt;/strong&gt;&lt;br&gt;
— template-setup.yaml&lt;br&gt;
— template-stackset.yaml&lt;br&gt;
&lt;strong&gt;tbs-devops-cfnhooks/&lt;br&gt;
— cfnhooks-lambda/&lt;/strong&gt;&lt;br&gt;
— — app.js&lt;br&gt;
— — package.json&lt;br&gt;
&lt;strong&gt;— cfnhooks-loggroup/&lt;/strong&gt;&lt;br&gt;
— — app.js&lt;br&gt;
— — package.json&lt;br&gt;
— template.yaml&lt;/p&gt;

&lt;p&gt;Further details of these are as follows:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A. Lambda Hooks Template (template.yaml)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This defines the CloudFormation hook resources, their corresponding lambda functions and required IAM roles to be implemented in target AWS accounts, as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;tbs-devops-cfnhooks&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;LambdaExecutionRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::IAM::Role&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;
      &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lambda.amazonaws.com'&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sts:AssumeRole&lt;/span&gt;
      &lt;span class="na"&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"&lt;/span&gt;

  &lt;span class="na"&gt;LambdaHookExecutionRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::IAM::Role&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;
      &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hooks.cloudformation.amazonaws.com'&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sts:AssumeRole&lt;/span&gt;
            &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;StringEquals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws:SourceAccount"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${AWS::AccountId}&lt;/span&gt;
              &lt;span class="na"&gt;ArnLike&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws:SourceArn"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:type/hook/*"&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${AWS::StackName}-LambdaHookExecutionRole&lt;/span&gt;
          &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2012-10-17"&lt;/span&gt;
            &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
                &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda:InvokeFunction&lt;/span&gt;
                &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;LambdaCfnHookFunction.Arn&lt;/span&gt;

  &lt;span class="na"&gt;LambdaHook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CloudFormation::LambdaHook&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Alias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Tbs::Devops::LambdaCfnHooks&lt;/span&gt;
      &lt;span class="na"&gt;ExecutionRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;LambdaHookExecutionRole.Arn&lt;/span&gt;
      &lt;span class="na"&gt;FailureMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FAIL&lt;/span&gt;
      &lt;span class="na"&gt;HookStatus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENABLED&lt;/span&gt;
      &lt;span class="na"&gt;LambdaFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;LambdaCfnHookFunction.Arn&lt;/span&gt;
      &lt;span class="na"&gt;TargetFilters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;TargetNames&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS::Lambda::Function&lt;/span&gt;
        &lt;span class="na"&gt;Actions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CREATE&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;UPDATE&lt;/span&gt;
        &lt;span class="na"&gt;InvocationPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PRE_PROVISION&lt;/span&gt;
      &lt;span class="na"&gt;TargetOperations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;STACK&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RESOURCE&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CHANGE_SET&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CLOUD_CONTROL&lt;/span&gt;

  &lt;span class="na"&gt;LogGroupHook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CloudFormation::LambdaHook&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Alias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Tbs::Devops::LogGroupCfnHooks&lt;/span&gt;
      &lt;span class="na"&gt;ExecutionRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;LambdaHookExecutionRole.Arn&lt;/span&gt;
      &lt;span class="na"&gt;FailureMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FAIL&lt;/span&gt;
      &lt;span class="na"&gt;HookStatus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENABLED&lt;/span&gt;
      &lt;span class="na"&gt;LambdaFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;LogGroupCfnHookFunction.Arn&lt;/span&gt;
      &lt;span class="na"&gt;TargetFilters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;TargetNames&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS::Logs::LogGroup&lt;/span&gt;
        &lt;span class="na"&gt;Actions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CREATE&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;UPDATE&lt;/span&gt;
        &lt;span class="na"&gt;InvocationPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PRE_PROVISION&lt;/span&gt;
      &lt;span class="na"&gt;TargetOperations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;STACK&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RESOURCE&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CHANGE_SET&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CLOUD_CONTROL&lt;/span&gt;

  &lt;span class="na"&gt;LambdaCfnHookFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Lambda::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Architectures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;x86_64&lt;/span&gt;
      &lt;span class="na"&gt;Code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cfnhooks-lambda/&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app.handler&lt;/span&gt;
      &lt;span class="na"&gt;MemorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;256&lt;/span&gt;
      &lt;span class="na"&gt;Role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;LambdaExecutionRole.Arn&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs20.x&lt;/span&gt;
      &lt;span class="na"&gt;ReservedConcurrentExecutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
      &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;

  &lt;span class="na"&gt;LogGroupCfnHookFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Lambda::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Architectures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;x86_64&lt;/span&gt;
      &lt;span class="na"&gt;Code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cfnhooks-loggroup/&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app.handler&lt;/span&gt;
      &lt;span class="na"&gt;MemorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;256&lt;/span&gt;
      &lt;span class="na"&gt;Role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;LambdaExecutionRole.Arn&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs20.x&lt;/span&gt;
      &lt;span class="na"&gt;ReservedConcurrentExecutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
      &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;B. Sample Lambda code(cfnhooks-loggroup)&lt;/strong&gt;&lt;br&gt;
The following code illustrates implementation for a LogGroup inspection lambda hook, which checks to ensure that log groups are encrypted with a KmsKey, and that a retention period has been set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;targetModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;requestData&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;targetModel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;targetName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;requestData&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;targetName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hookStatus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LogGroup is correctly configured.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clientRequestToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientRequestToken&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetName&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AWS::Logs::LogGroup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;retentionInDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetModel&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;resourceProperties&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;RetentionInDays&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;kmsKeyId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetModel&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;resourceProperties&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;KmsKeyId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;errorMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;retentionInDays&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;errorMessage&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LogGroup RetentionInDays must be present.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;kmsKeyId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;errorMessage&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LogGroup KmsKeyId must be present.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hookStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FAILED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errorCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NonCompliant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;C. StackSet Template (deploy-setup/template-stackset.yaml):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;This template creates an S3 bucket for storing built lambda artefacts for devops deployments, which may be accessed&lt;/span&gt;
  &lt;span class="s"&gt;by other accounts within this AWS Organization&lt;/span&gt;

&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;TargetOrgUnitIds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Target organization units for deploying solution&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CommaDelimitedList&lt;/span&gt;

  &lt;span class="na"&gt;StackSetName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tbs-devops-cfnhooks-stackset&lt;/span&gt;

  &lt;span class="na"&gt;TemplateUrl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S3 URL of CF template which defined our SAM solution for lambda hooks&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CfnLambdaHookStackset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CloudFormation::StackSet&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AutoDeployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;RetainStacksOnAccountRemoval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;CallAs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DELEGATED_ADMIN&lt;/span&gt; &lt;span class="c1"&gt;# Required when deploying to delegated admin accounts for an org&lt;/span&gt;
      &lt;span class="na"&gt;Capabilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;CAPABILITY_IAM&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;CAPABILITY_NAMED_IAM&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;PermissionModel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SERVICE_MANAGED&lt;/span&gt;
      &lt;span class="na"&gt;StackInstancesGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Regions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ap-southeast-2&lt;/span&gt;
          &lt;span class="na"&gt;DeploymentTargets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;OrganizationalUnitIds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TargetOrgUnitIds&lt;/span&gt;
      &lt;span class="na"&gt;StackSetName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;StackSetName&lt;/span&gt;
      &lt;span class="na"&gt;TemplateURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;TemplateUrl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;D. StackSet Deployment Bash Script&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;stackName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tbs-devops-cfnhooks-stackset"&lt;/span&gt;
&lt;span class="nv"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ap-southeast-2"&lt;/span&gt;
&lt;span class="nv"&gt;toolsAccountId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1234"&lt;/span&gt; &lt;span class="c"&gt;# To replace with real value&lt;/span&gt;
&lt;span class="nv"&gt;targetOrgUnitIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ou-234,ou-34534"&lt;/span&gt; &lt;span class="c"&gt;# To replace with real values&lt;/span&gt;
&lt;span class="nv"&gt;deployBucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"lambdacfnhooks-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;toolsAccountId&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-deploybucket"&lt;/span&gt;
&lt;span class="nv"&gt;currentTime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s2"&gt;"%Y%m%d%H%M%S"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; ../tbs-devops-cfnhooks

&lt;span class="c"&gt;# First we package our cfn-hooks lambda solution&lt;/span&gt;
aws cloudformation package &lt;span class="nt"&gt;--template-file&lt;/span&gt; template.yaml &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--s3-bucket&lt;/span&gt; &lt;span class="nv"&gt;$deployBucket&lt;/span&gt; &lt;span class="nt"&gt;--s3-prefix&lt;/span&gt; &lt;span class="nv"&gt;$stackName&lt;/span&gt; &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$region&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--output-template-file&lt;/span&gt; generated-template.yaml &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--profile&lt;/span&gt; thebetterstore-devopsaccount

&lt;span class="c"&gt;# Next export our generated template to S3&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; ./generated-template.yaml s3://&lt;span class="nv"&gt;$deployBucket&lt;/span&gt;/generated-template.yaml &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--profile&lt;/span&gt; thebetterstore-tools


&lt;span class="c"&gt;# Next deploy the stackset, defined in IaC (Cfn) to our tools account, which will then manage deployment of the hooks to accounts&lt;/span&gt;
&lt;span class="c"&gt;# within specified target OU's. NB use lastupdatedtime tag to ensure deployment/that changes are detected&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ../deploy-setup
aws cloudformation deploy &lt;span class="nt"&gt;--template&lt;/span&gt; template-stackset.yaml &lt;span class="nt"&gt;--stack-name&lt;/span&gt; &lt;span class="nv"&gt;$stackName&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--capabilities&lt;/span&gt; CAPABILITY_IAM CAPABILITY_NAMED_IAM &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$region&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--parameter-overrides&lt;/span&gt; &lt;span class="nv"&gt;TemplateUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://&lt;/span&gt;&lt;span class="nv"&gt;$deployBucket&lt;/span&gt;&lt;span class="s2"&gt;.s3.ap-southeast-2.amazonaws.com/generated-template.yaml"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;TargetOrgUnitIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;targetOrgUnitIds&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nv"&gt;lastupdatedtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$currentTime&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--profile&lt;/span&gt; thebetterstore-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The end result from deployment should be the observation of the following in the AWS Cloudformation UI of target accounts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7ty9o9edt1467pe3wo5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7ty9o9edt1467pe3wo5.png" alt=" " width="720" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally we can check our hook functionality by attempting to deploy a stack containing non-compliant resources. For this exercise, I attempted to deploy a new stack called tbs-devops-cfnhooktest in an AWS account belonging to a target Organization Unit, which was comprised of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;LogGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Logs::LogGroup&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;LogGroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ANonCompliantLogGroup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;i.e. containing a log group missing the required retention period and KmsKeyId properties.&lt;/p&gt;

&lt;p&gt;Attempting to deploy this resulted in the following errors being thrown by our new Cloudformation LogGroup hook:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmuvwb4rw0tu2syq8bd4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftmuvwb4rw0tu2syq8bd4.png" alt=" " width="720" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Traditional methods for helping to ensure compliance of automatically-deployed resources using Cloudformation/CDK in AWS have generally included developer training, code reviews, ‘shift-left’ guards in deployment pipelines, and detective controls using AWS Config and/or SecurityHub. It is however still very easy for non-compliant configurations to be missed and creep in, particularly in cases where guards or other controls may not be available for an organization’s internal standards such as naming conventions. Security, reliability, performance and maintainability of deployed solutions can all suffer as a result of standards not being met.&lt;/p&gt;

&lt;p&gt;The use of Custom Lambda Hooks as preventative controls however look very promising to tackle this challenge; which offer great flexibility for implementing checks or naming conventions that may be required, and generation of custom and meaningful errors when there are issues. Furthermore their implementation and deployment across accounts within AWS Organizations proved fairly straight-forward in this exercise. Targeting Lambda Hooks deployments against Organization Units within AWS Organizations, using ‘Self-Managed’ StackSets also means that any new accounts created within these will also be automatically configured.&lt;/p&gt;

&lt;p&gt;It may be that development OU’s should be targeted initially to ensure that compliance issues of stacks are caught during their maintence, and not for example during an emergency hotfix against production! However with the gradual implementation of the hooks across environments, hopefully AWS Config non-compliance alerts may become a thing of the past!&lt;/p&gt;

&lt;p&gt;Code used in this post is also available via my public GitHub repository at: &lt;a href="https://github.com/TheBetterStore/tbs-devpops-cfnhooks" rel="noopener noreferrer"&gt;https://github.com/TheBetterStore/tbs-devpops-cfnhooks&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cloudformation-cli/latest/hooks-userguide/hooks-concepts.html" rel="noopener noreferrer"&gt;AWS CloudFormation Hooks concepts&lt;/a&gt;, &lt;em&gt;AWS documentation (web)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cfn-guard/latest/ug/writing-rules.html" rel="noopener noreferrer"&gt;Writing AWS CloudFormation Guard rules&lt;/a&gt;, &lt;em&gt;AWS documentation (web)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/11/aws-cloudformation-hooks-custom-aws-lambda-functions/" rel="noopener noreferrer"&gt;AWS CloudFormation Hooks now support custom AWS Lambda functions&lt;/a&gt;, &lt;em&gt;AWS documentation (web)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-orgs-activate-trusted-access.html?icmpid=docs_cfn_console" rel="noopener noreferrer"&gt;Activate trusted access for stack sets with AWS Organizations&lt;/a&gt;, &lt;em&gt;AWS documentation (web)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://us-east-1.console.aws.amazon.com/organizations/v2/home/accounts" rel="noopener noreferrer"&gt;Create a resource-based delegation policy with AWS Organizations&lt;/a&gt;, &lt;em&gt;AWS documentation (web)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/weareservian/building-the-better-store-a-global-resilient-and-agile-cloud-native-ecommerce-system-using-9e18d32cd8fa" rel="noopener noreferrer"&gt;Building “The Better Store” an agile cloud-native ecommerce system on AWS — Part 1: Introduction to Microservice Architecture&lt;/a&gt;, &lt;em&gt;Bryce Cummock (medium/web)&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Disclaimer&lt;/em&gt;: The views and opinions expressed in this article are those of the author only.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudformation</category>
      <category>devops</category>
    </item>
    <item>
      <title>Building “The Better Store” an agile cloud-native ecommerce system on AWS — Part 2: Defining DDD Strategic Patterns</title>
      <dc:creator>BrycePC</dc:creator>
      <pubDate>Tue, 17 Mar 2026 07:59:20 +0000</pubDate>
      <link>https://forem.com/aws-builders/building-the-better-store-an-agile-cloud-native-ecommerce-system-on-aws-part-2-defining-ddd-3hn5</link>
      <guid>https://forem.com/aws-builders/building-the-better-store-an-agile-cloud-native-ecommerce-system-on-aws-part-2-defining-ddd-3hn5</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/brycepc/building-the-better-store-an-agile-cloud-native-ecommerce-system-on-aws-part-1-introduction-to-27ii"&gt;Part One&lt;/a&gt; of Building the Better Store, I provided an introduction of an envisaged eCommerce website, and how Domain Driven Design (DDD) and its Strategic Patterns may be used to help compose a decoupled Microservices Architecture (MSA) that would promote its future change agility, scalability and fault-tolerance.&lt;/p&gt;

&lt;p&gt;This article focuses on illustrating and using these patterns for design of The Better Store.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introducing Domain Driven Design
&lt;/h2&gt;

&lt;p&gt;Domain Driven Design (DDD) is a methodology initially defined in 2003 in Eric Evan’s popular and well-received book &lt;em&gt;‘Domain-Driven Design: Tackling Complexity in the Heart of Software’&lt;/em&gt; [1]. It is aimed at decomposing the complexity of a &lt;strong&gt;Problem Space&lt;/strong&gt; (that is, a business’s functional domain/capabilities that are requiring development) into a&lt;br&gt;
domain model comprised of one or more decoupled and cohesive &lt;strong&gt;Bounded Contexts&lt;/strong&gt;; being sets of well-organised definitions of objects and business rules that share the same ubiquitous language understood by both business stakeholders and technical staff, to offer easier interpretation and design of software. These Bounded Contexts define the problem’s &lt;strong&gt;Solution Space&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;While DDD predates the advent of microservices by almost 10 years, its goals and defined patterns for designing systems as cohesive, decoupled and collaborative bounded contexts are strongly aligned with microservice architecture design principles, for implementing cohesive and decoupled services that realize advantages of change agility, resilience and scalability within cloud environments. As such, DDD has become a popular reference today when discussing microservice architectures.&lt;/p&gt;




&lt;p&gt;Domain-driven design is aimed at promoting system design quality via the following methods:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A. Using Strategic Patterns to:​&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Distill the &lt;strong&gt;Problem Space&lt;/strong&gt;, i.e. an organisation’s business capabilities, to identify features important to the business by:

&lt;ul&gt;
&lt;li&gt;Using ‘Knowledge Crunching’ methods to extract relevant information and help identify and decompose the problem space into separate manageable subdomains.&lt;/li&gt;
&lt;li&gt;Defining a Shared/Ubiquitous Language (UL) for each subdomain that is understood by both business and technical stakeholders, which should subsequently be used for documentation and code artefacts (e.g. for the names of classes and their methods and attributes).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Produce a &lt;strong&gt;Solution Space&lt;/strong&gt; comprising &lt;strong&gt;Bounded Contexts&lt;/strong&gt; to demarcate and model solutions for identified subdomains. This should include a Context Map that defines relationships between the bounded contexts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example (with credit to Millett &amp;amp; Tune [3]):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx84gk1dkupeibu0nupp9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx84gk1dkupeibu0nupp9.png" alt=" " width="720" height="410"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 1: Illustrating DDD Strategic Patterns for defining a Problem Space&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ferjiz9apxfbf0yn7pjad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ferjiz9apxfbf0yn7pjad.png" alt=" " width="720" height="422"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Figure 2: DDD patterns for a corresponding Solution Space&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B. Using Tactical Patterns to:​&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an effective Object-Oriented domain model for each Bounded Context, using defined patterns such as Entity, Aggregate, Value Object, Service, Factory and Repository.&lt;/li&gt;
&lt;li&gt;Illustrate emerging patterns of domain events and event sourcing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This article will next focus on application of Strategic Patterns for design of The Better Store. The application of tactical patterns will be the focus in my next article: &lt;em&gt;Part 3: Defining DDD Tactical Patterns.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Better Store &amp;amp; The Problem Space
&lt;/h2&gt;

&lt;p&gt;As an initial step in the design of The Better Store, we would expect one or more appointed business domain experts to collaborate with the development team in ‘Knowledge Crunching’ sessions, using popular techniques such as ‘Event Storming’ for fast sharing and discussion of ideas for desired functional behaviour. Event Storming has been touted as having much success in recent years, for sharing ideas of system behaviour and scope between both technical and non-technical people [4].&lt;/p&gt;

&lt;p&gt;The output of Event Storming workshops are highly-visual diagrams conveying envisaged business contexts and behaviours. From here, main use cases may be chosen that will become part of the ‘Core Domain’ for implementation of a Minimal Viable Product (MVP). More explicit specifications for these can then be defined using tools such as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Behaviour-Driven Development (BDD) specifications to focus on the behaviour of the main use cases oriented around specific scenarios.&lt;/li&gt;
&lt;li&gt;Class Responsibility Collaborator (CRC) cards to define potential structural elements from these, and the interactions between them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The application of these techniques will be illustrated at a very high-level below for an MVP definition of The Better Store. Further reading is recommended for those that wish to learn more of the techniques; the scope for each of these, in particular Event Storming and BDD is large!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NB: the methodologies chosen here have been selected as examples for The Better Store as a new application. Other techniques would undoubtedly also come into play for other scenarios; for example, exploring published models for similar applications, or referencing existing documentation for a current application that is to be enhanced or refactored.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A. Identifying System Behaviour Using Event Storming&lt;/strong&gt;&lt;br&gt;
Alberto Brandolini first introduced this methodology in his book Introducing Event Storming [3], whereby analysis of a business process involves bringing together the development team and the right business process expert(s) from applicable “knowledge silos” within the business as participants in collaboration workshops. Within the workshops, participants are requested by the workshop mediator to use different colour-coded sticky notes to model a flow of domain events, commands and external systems over time (from left to right) i.e. &lt;em&gt;temporal modelling&lt;/em&gt; on a clear wall space. Online collaboration tools such as &lt;a href="http://miro.com/" rel="noopener noreferrer"&gt;Miro &lt;/a&gt; are also available to simulate this activity for teams where participants are in remote locations.&lt;/p&gt;

&lt;p&gt;Event storming is often performed in a number of phases as defined and controlled by the mediator as appropriate for the system and/or goal; for example for a high-level design of The Better Store we will derive these from Brandolini’s ‘Big Picture Workshop’ configuration to use:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1. Chaotic Exploration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All participants add orange notes to represent expected domain events (verbs in the past tense), and lilac notes to represent questions or unknowns (specific discussions should be kept short at this time; our focus here is coverage).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Phase 2. Timeline Enforcement&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Events are rearranged to restrict them to a single timeline. This should result in further discussions and help identify gaps or unknowns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Phase 3. Commands, People and Systems&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Blue notes&lt;/em&gt; are added to represent commands, which may be called by external users or systems to generate the events.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Pink notes&lt;/em&gt; are then added to represent external systems that may be used.&lt;/li&gt;
&lt;li&gt;Finally, &lt;em&gt;yellow notes&lt;/em&gt; with a User icon are added to represent people that interact with the system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Phase 4. Explicit Walkthrough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Different narrators take the lead for describing the behaviour for different portions of the system, for further review by other participants.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 5. Identifying Aggregates&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can use &lt;em&gt;yellow notes&lt;/em&gt; to denote potential ‘Aggregates’; that is, system entities that have their own identity id, sub-elements and transactional lifecycle (we will be discussing these more in the next Tactical Patterns article). An example is Order, which will be required to have its own unique id for storage within a database, alongside its selected product items as attributes (which would also be persisted when an order is created or updated in a data store).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 6. Problems and Opportunities&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Additional time for participants to add further questions (lilac notes), or opportunities (&lt;em&gt;green notes&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 7. Wrap up&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ensure photos of the board are taken (and notes are kept if hard to ready from photos), to allow its referencing for analysis and modelling later.&lt;/p&gt;

&lt;p&gt;The following provides an example output from a remotely-run &lt;em&gt;(using Miro)&lt;/em&gt; Event Storming session for The Better Store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi3dtnwufs2tbcjhixl93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi3dtnwufs2tbcjhixl93.png" alt=" " width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Figure 3: Event Storming sample output for The Better Store&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B. Defining Functional Behaviour with Behaviour Driven Development (BDD) Specifications&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;BDD is a software development process based on Test Driven Development (TDD), which focuses on defining explicitly functional scenarios and required behaviour, using its own ‘GWT’ (Given, When, Then) specification&lt;br&gt;
language. This format is also intended to provide a language that is understood by both business and technical stakeholders for defining requirements, oriented around user stories, or ‘features’. Examples for “The Better Store” generated as BDD specifications may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.thebetterstore.net/docs/domain-driven-design/bdd/AddProductsToCart.feature" rel="noopener noreferrer"&gt;AddProductsToCart.feature&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.thebetterstore.net/docs/domain-driven-design/bdd/AdministerErrors.feature" rel="noopener noreferrer"&gt;AdministerErrors.feature&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.thebetterstore.net/docs/domain-driven-design/bdd/CheckoutProductsInCart.feature" rel="noopener noreferrer"&gt;CheckoutProductsInCart.feature&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.thebetterstore.net/docs/domain-driven-design/bdd/UserLogin.feature" rel="noopener noreferrer"&gt;UserLogin.feature&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.thebetterstore.net/docs/domain-driven-design/bdd/UserSignup.feature" rel="noopener noreferrer"&gt;UserSignup.feature&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.thebetterstore.net/docs/domain-driven-design/bdd/FulfillOrder.feature" rel="noopener noreferrer"&gt;FulfillOrder.feature&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.thebetterstore.net/docs/domain-driven-design/bdd/ManageOrder.feature" rel="noopener noreferrer"&gt;ManageOrder.feature&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.thebetterstore.net/docs/domain-driven-design/bdd/MaintainProductsInCart.feature" rel="noopener noreferrer"&gt;MaintainProductsInCart.feature&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.thebetterstore.net/docs/domain-driven-design/bdd/ViewOrderReports.feature" rel="noopener noreferrer"&gt;ViewOrderReports.feature&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.thebetterstore.net/docs/domain-driven-design/bdd/ViewProductCatalogue.feature" rel="noopener noreferrer"&gt;ViewProductCatalogue.feature&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using this method of capturing requirements removes the ambiguity that traditional requirements documentation can result in while also focusing on the domain language. The generation of these can also help with formulation of a subdomain’s Ubiquitous Language [2].&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C. Defining Functional Components with Class Responsibility Collaborator (CRC) cards&lt;/strong&gt;&lt;br&gt;
CRC Card modelling is an object-oriented technique that involves identifying the main system actors, e.g. from the Aggregates first deduced during Event Storming or entities noted in the BDD specifications above, and representing these visually for easy collaboration on separate cards.&lt;br&gt;
Each CRC card should capture the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A class name, which represents a known concept within the domain and is easily understood by business and technical members (this will go into our Ubiquitous Language).&lt;/li&gt;
&lt;li&gt;Class responsibilities.&lt;/li&gt;
&lt;li&gt;Associated classes. A class often does not have sufficient information to fulfil its responsibilities and must collaborate with other classes to complete their task. Such collaboration may be either: i. a request for information from another class, or ii. a command to perform an action.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;e.g.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy45ymyyq09qbwek2kldl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy45ymyyq09qbwek2kldl.png" alt=" " width="720" height="711"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Figure 4: CRC card examples for The Better Store&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Distilling the problem space​ into subdomains
&lt;/h2&gt;

&lt;p&gt;The above exercises, in particular Event Storming and CRC cards assist us with demarcating functional requirements into separate subdomains, identifying dependencies between them, and an initial Ubiquitous Language for each. As a final step in defining our problem space, we would like to further categorise these in order of importance to our business. The outcome of this exercise is to prioritise functionality for development focus, to ensure that tasks that provide the most competitive advantage receive the most attention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Core Subdomains​&lt;/strong&gt;&lt;br&gt;
These cover the most important part of the business that provides its competitive advantage. Identification of the core domain(s) here helps provide clarity of the software that should receive the greatest development focus. For The Better Store these have been identified as:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpwy3p3nyxrrh5vi8ndm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpwy3p3nyxrrh5vi8ndm.png" alt=" " width="720" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supporting Subdomains​&lt;/strong&gt;&lt;br&gt;
These provide supporting functions to the core domains. If possible, Commercial Off The Shelf (COTS) products should also be used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh2pmlhawcjs9y7ku1ep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh2pmlhawcjs9y7ku1ep.png" alt=" " width="720" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generic Subdomains&lt;/strong&gt;&lt;br&gt;
Generic subdomains provide common functionality that are not core to the business and could also be provided by COTS software (again freeing-up developers to focus on the core areas).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff5mcdv9e030szh92y143.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff5mcdv9e030szh92y143.png" alt=" " width="720" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following diagram summarises the subdomains identified for our problem space, and their dependencies:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzmtq8g2ibovnnvq2cpy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzmtq8g2ibovnnvq2cpy.png" alt=" " width="720" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Figure 5: High-level domain model&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the Solution Space​
&lt;/h2&gt;

&lt;p&gt;The Solution Space provides a model for realizing the needs of the requirements given in the Problem Space, for example by defining appropriate &lt;em&gt;Bounded Contexts&lt;/em&gt; (BC’s) for each subdomain to implement. Each bounded context is also provided with its own Ubiquitous&lt;br&gt;
Language; much of which should have been defined during analysis of the Problem Space for the belonging subdomains. In this way each bounded context is kept cohesive to a specific functional area.&lt;/p&gt;

&lt;p&gt;Following the modelling of Bounded Contexts, the solution space also defines &lt;em&gt;Context Mapping&lt;/em&gt;, using &lt;em&gt;Integration Patterns&lt;/em&gt; to define the collaboration relationships between these. This is an important output which will highlight the degree of decoupling between services, and the shared knowledge required by the teams that own them.&lt;/p&gt;

&lt;p&gt;DDD’s Integration Patterns are described below [1, 10]:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2epmm3lcdsmurhoesgqx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2epmm3lcdsmurhoesgqx.png" alt=" " width="720" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Figure 6: DDD integration patterns&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;where the patterns may be categorised and described below. Context Mapping annotations are also provided below; their usage is illustrated in a sample context mapping diagram for The Better Store that follows.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Symmetric Patterns&lt;/strong&gt;: where 2 BC’s have related interdependencies.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Separate Ways&lt;/strong&gt;; BC’s are independent with no relationships between them. Teams can work at their own pace. This is an ideal scenario!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partnership&lt;/strong&gt;; 2 BC’s are mutually dependent on each-other; i.e., they are tightly-coupled. Teams need to know the business models and UL of the other team. Changes to 1 BC need to be coordinated between teams. These are an anti-pattern and should be avoided where possible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Kernel&lt;/strong&gt;; A move to demarcate shared models used between BC’s. e.g., as a separate shared library and UL. This should be kept to a minimal number of contexts.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Asymmetric Patterns&lt;/strong&gt;: where one BC is dependent on another. The dependent BC is termed Downstream (D), whereas the provider/host is termed Upstream (U). BC’s in D hence have knowledge of models in U BC’s.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Customer-Supplier&lt;/strong&gt;; An upstream BC exposes models specifically for the needs of a downstream BC; i.e., in a client/host relationship.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conformist&lt;/strong&gt;; An upstream BC exposes models with no regards to any downstream BC. The downstream BC conforms to the upstream BC’s models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anti-Corruption Layer (ACL)&lt;/strong&gt;; where a downstream BC is NOT conformist; an isolated transformation layer is used to protect the downstream BC from corruption, i.e. from using the upstream domain’s model. The ACL only has the knowledge of models from U and D to perform necessary mappings from U’s model to the downstream BC (D).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;One-to-many Patterns​&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open-Host Service (OHS)&lt;/strong&gt;; the upstream provider/OHS offers common services to other BC’s. The downstream BC’s may choose to either conform to U, or use an ACL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Published Language (PL)&lt;/strong&gt;; The OHS provides a common language accepted by a downstream BC. These are denoted as OHS | PL for Upstream in a context mapping diagram. OpenAPI specifications for REST API’s are an example of these.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note we want to avoid the ‘Big Ball of Mud’; this is the described anti-pattern which often results from unchecked growth of a monolith over time, where without practices code can become unstructured and very hard to extend and maintain.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A context mapping diagram for The Better Store may be modeled as:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feiekvjg032uv2o2p6oja.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feiekvjg032uv2o2p6oja.png" alt=" " width="720" height="223"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Figure 7: A context map for The Better Store&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps: Creating a Model Driven Design
&lt;/h2&gt;

&lt;p&gt;The strategic patterns shown above have provided a high-level design of expected service decomposition as Bounded Contexts and the relations between them. Key outputs are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Decomposition of bounded contexts; with each having its own Ubiquitous Language and business rules definitions.&lt;/li&gt;
&lt;li&gt;Context mapping, to define required relationships between them, with maximum decoupling being in favour.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As can be seen, DDD is a broad and involved methodology, and there are many tools available for decomposing the problem space to define the required outputs. Its full application may be better recommended for working with large and complex domains that are difficult to manage with other techniques [11], however I believe many of the discussed methodologies here such as Event Storming, CRC cards, Ubiquitous Language definitions and references to decoupled integration patterns would prove valuable for defining microservice architectures for less complex domains also.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coming next in Part Three: Defining DDD Tactical Patterns&lt;/strong&gt;. This will explore the more technical and object-oriented modelling of bounded contexts to complete the solution space. We will also be drilling into the tactical patterns for the realization of cohesive and decoupled microservices for The Better Store.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;“Domain Driven Design, Tackling Complexity in the Heart of Software”, Evans, E., Addison-Wesley (2003)&lt;/li&gt;
&lt;li&gt;“Patterns, Principles, and Practices of Domain-Driven Design”, Millett &amp;amp; Tune, Wiley &amp;amp; Sons (2015)&lt;/li&gt;
&lt;li&gt;“Introducing Event Storming”, Brandolini, A., LeanPub.com (2021)
“Event Storming: Collaborative Learning for Complex Domains”, Rayner, P., 4. Talk in Saturn (2017), &lt;a href="https://www.youtube.com/watch?v=vf6xoi2d9VE" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=vf6xoi2d9VE&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;“Practical Event-Driven Microservices Architecture”, Rocha, H., Apress (2022)&lt;/li&gt;
&lt;li&gt;“Open Agile Architecture”, The Open Group, &lt;a href="https://pubs.opengroup.org/architecture/o-aa-standard/index.html" rel="noopener noreferrer"&gt;https://pubs.opengroup.org/architecture/o-aa-standard/index.html&lt;/a&gt; (2020)&lt;/li&gt;
&lt;li&gt;“Implementing Domain Driven Design”, Vernon, V., Addison-Wesley (2013)&lt;/li&gt;
&lt;li&gt;“Building Microservices”, Newman, S., O’Reilly (2015)&lt;/li&gt;
&lt;li&gt;“Big Ball of Mud”, Foote &amp;amp; Yoder (University of Illinois), web (1999)&lt;/li&gt;
&lt;li&gt;“Domain Driven Design &amp;amp; Microservices for Architects”, Sakhuja, R., Udemy (2021)&lt;/li&gt;
&lt;li&gt;“Microsoft Application Architecture Guide, 2nd ed”, Microsoft, web (2013)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Disclaimer:&lt;/em&gt; The views and opinions expressed in this article are those of the author only.&lt;/p&gt;

</description>
      <category>cloudnative</category>
      <category>microservices</category>
      <category>ddd</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
