Proyecto final del curso de Arquitecto de Soluciones AWS. Monorepo con Angular, Lambdas y CDK.
apps/web: frontend Angular 21apps/backend: Lambdas y contrato OpenAPIapps/infra: infraestructura CDK (VPC, CloudFront, Route53, WAF, Cognito, DynamoDB, S3)https://finalweb.<tu-dominio>https://finalapi.<tu-dominio>Los dominios se definen via contexto CDK o variables de entorno (ROOT_DOMAIN_NAME, API_DOMAIN_NAME, WEB_DOMAIN_NAME).
El frontend lee la URL de la API desde window.__env.apiBaseUrl cargado en apps/web/src/assets/env.js. En despliegues, reemplaza ese archivo (o inyecta la variable) para apuntar a tu API. En local, el fallback es http://localhost:3000.
Ver ARCHITECTURE.md y architecture.drawio.
Diagrama (Mermaid) basado en architecture.drawio:
flowchart LR
user((Usuario)) --> web[Angular Web]
web --> cfWeb[CloudFront Web]
cfWeb --> s3web[S3 WebBucket]
web --> cfApi[CloudFront API]
cfApi --> api[API Gateway HTTP API]
api --> auth[Lambda Auth]
api --> products[Lambda Products]
api --> orders[Lambda Orders]
orders --> ddb[DynamoDB Single Table]
ddb --> stream[DynamoDB Stream]
stream --> orderStream[Lambda Order Stream]
orderStream --> sns[SNS Orders Topic]
sns --> sqsEmail[SQS Orders Queue]
sqsEmail --> orderEmail[Lambda Order Email]
orderEmail --> ses[SES Template]
orderEmail --> s3email[S3 EmailsBucket]
sns --> sqsLake[SQS Orders Lake Queue]
sqsLake --> orderLake[Lambda Order Lake]
orderLake --> kinesis[Kinesis Data Stream]
kinesis --> firehose[Firehose]
firehose --> s3data[S3 DataBucket]
subgraph Edge
waf[WAF]
r53[Route53]
end
r53 --> cfWeb
r53 --> cfApi
waf --> cfWeb
waf --> cfApiLas Lambdas usan AWS X-Ray para trazabilidad distribuida.
| Infra | Web | Backend |
|---|---|---|
npx nx run web:servenpx nx run @org/backend:build:production
npx nx run @org/infra:build:production
npx nx run web:build:productionLa documentación vive en docs-site/ (Astro Starlight) y docs/api-typedoc/
(TypeDoc).
GitHub Pages publica las docs en:
Badges de documentación:
npm run docs:dev
npm run docs:check
npm run docs:coveragePara actualizar el contrato OpenAPI publicado en Starlight:
Example :npm run docs:sync-openapiPara generar el sitio estático de documentación:
Example :npm run docs:buildPara levantar el preview local del build estático:
Example :npm run docs:previewGuías en Starlight:
docs-site/src/content/docs/astro.mdxdocs-site/src/content/docs/compodoc.mddocs-site/src/content/docs/typedoc.mdxPublicado en GitHub Pages: https://fjbatresv.github.io/sdg19-final/
Example :npx nx run web:docs
npx nx run web:docs:coverage
npx nx run web:docs:checkGuia completa: docs-site/src/content/docs/compodoc.md.
Los tests se ejecutan por proyecto con cobertura >= 80% y generan LCOV en:
coverage/apps/backend/lcov.infocoverage/apps/web/lcov.infocoverage/apps/infra/lcov.infonpm run test:ci
npx nx run backend:test --configuration=ci
npx nx run web:test --configuration=ci
npx nx run infra:test --configuration=ciNotas:
web:test --configuration=ci corre unit tests (Vitest). No hay E2E configurado todavía.Guía completa: docs/testing.md.
export AWS_PROFILE=xxxx
AWS_REGION=<PRINCIPAL_REGION> npx cdk bootstrap aws://<AWS_ACCOUNT_ID>/<PRINCIPAL_REGION> -c hostedZoneId=<HOSTED_ZONE_ID>
AWS_REGION=<SECONDARY_REGION> npx cdk bootstrap aws://<AWS_ACCOUNT_ID>/<SECONDARY_REGION> -c hostedZoneId=<HOSTED_ZONE_ID>
AWS_REGION=<SECONDARY_REGION> npx cdk deploy Sdg19ReplicaStack --require-approval never -c hostedZoneId=<HOSTED_ZONE_ID>
AWS_REGION=<PRINCIPAL_REGION> npx cdk deploy Sdg19PrimaryStack --require-approval never -c hostedZoneId=<HOSTED_ZONE_ID>Antes de desplegar, revisa DEPLOY.md para crear el Hosted Zone, el OIDC
provider y el role con su policy.
La carpeta oidc_role/ incluye JSON de referencia para crear un role OIDC
en otra cuenta y reutilizar este pipeline:
oidc_role/identity-provider.json: datos del Identity Provider OIDC.oidc_role/trust-relationship.json: trust policy para GitHub Actions.oidc_role/permission-policy.json: permisos mínimos para CDK + deploy.Reemplaza los placeholders:
$ACCOUNT_ID por el ID de la cuenta destino.repo:<user>/<repository name>:* por el repo real (owner/nombre).Luego crea el role con ese trust policy y adjunta la policy de permisos.
El aws_role_name en el config_json debe coincidir con el nombre de ese role.
Ejemplo de config_json para el workflow deploy.yml (input config_json):
{
"aws_account_id": "123456789012",
"aws_region_primary": "us-east-1",
"aws_region_replica": "us-east-2",
"hosted_zone_id": "Z0ABCDEF1234567XYZ",
"domains": {
"root": "example.dev",
"api": "api.example.dev",
"web": "web.example.dev"
},
"ses": {
"from": "no-reply@example.dev",
"mail_from": "mail.example.dev"
},
"enable_lambda_vpc": false,
"aws_role_name": "sdg19-deploy-role"
}Campos del config_json:
aws_account_id: cuenta AWS donde se despliega.aws_region_primary: región primaria (stacks principales).aws_region_replica: región de réplica (stack secundario).hosted_zone_id: Hosted Zone ID de Route53 para los dominios.domains.root: dominio raíz (ej. tu-dominio.com).domains.api: dominio para API (ej. finalapi.tu-dominio.com).domains.web: dominio para web (ej. finalweb.tu-dominio.com).ses.from: remitente verificado en SES (ej. no-reply@tu-dominio.com).ses.mail_from: subdominio MAIL FROM (ej. mail.tu-dominio.com).enable_lambda_vpc: true/false para habilitar VPC en Lambdas.aws_role_name: nombre del role a asumir.emails_replica_kms_key_arn (opcional): ARN de la KMS de la réplica (si ya existe).El ARN del role se construye con aws_account_id + aws_role_name.
El stack crea la identidad de dominio SES, DKIM y MAIL FROM usando Route53.
Para enviar a cualquier destinatario necesitas sacar SES del sandbox.
La Lambda order-email valida formato básico de email, guarda una copia en S3
con estado pending antes del envío y la marca como sent al finalizar para
evitar reintentos duplicados.
Contexto recomendado en cdk.json:
rootDomainName: dominio raiz (ej: tu-dominio.com) o ROOT_DOMAIN_NAMEapiDomainName: dominio de API (ej: finalapi.tu-dominio.com) o API_DOMAIN_NAMEwebDomainName: dominio de web (ej: finalweb.tu-dominio.com) o WEB_DOMAIN_NAMEsesTemplateName: nombre de plantilla SES (ej: sdg19-order-confirmation)sesFromAddress: remitente (ej: noreply@tu-dominio) o SES_FROM_ADDRESSsesMailFromDomain: subdominio MAIL FROM (ej: mail.tu-dominio) o SES_MAIL_FROM_DOMAINEjemplo por CLI:
Example :AWS_REGION=<PRINCIPAL_REGION> npx cdk deploy Sdg19PrimaryStack \
-c hostedZoneId=<HOSTED_ZONE_ID> \
-c rootDomainName=tu-dominio.com \
-c apiDomainName=finalapi.tu-dominio.com \
-c webDomainName=finalweb.tu-dominio.com \
-c sesFromAddress=noreply@tu-dominio.com \
-c sesMailFromDomain=mail.tu-dominio.comDespues del deploy:
Los eventos de ordenes se publican en SNS y se derivan a una cola SQS dedicada.
Una Lambda consume esos mensajes, los escribe en un Kinesis Data Stream y un
Firehose los entrega al bucket de datos (DataBucket) bajo el prefijo
data-lake/orders/ en formato Parquet con particiones dinámicas
year=YYYY/month=MM/day=DD/hour=HH/.