dora.ed

Architecture

How I think about building systems — the principles behind every decision, the structure of the delivery platform, and the reasoning that connects security, automation, and simplicity into one coherent approach.

How I think before I build

These are not rules I follow — they are positions I hold. Every architectural decision in this platform traces back to one of them.

01
Security is not a layer — it is the foundation

Every system I build starts with the question: what is the minimum access needed for this to work? IAM OIDC over long-lived credentials. Private S3 origins over public buckets. OAC over legacy OAI. Least privilege is not a compliance checkbox — it is a design constraint applied from day one.

02
Infrastructure must be reproducible

If the only way to rebuild your infrastructure is to remember what you clicked in the console, it is already broken. Everything I provision is in Terraform — version-controlled, reviewable, and deployable from scratch in minutes. The infrastructure is code. It lives in the repo. It has a history.

03
Automation removes human error from the critical path

Manual deployments introduce variability. The tenth time someone runs a deploy, they will skip a step. CI/CD pipelines enforce the same process every time — lint, type-check, test, build, deploy — regardless of who triggers them or when. Consistency at the cost of flexibility is worth it.

04
Simplicity is an engineering decision, not a compromise

The primary deployment path for this platform is S3 + CloudFront, not Kubernetes. That is intentional. Kubernetes adds real operational cost — nodes to manage, probes to tune, networking to debug. For a static site, that cost buys nothing. Use the simplest tool that meets the requirement. Add complexity only when the problem demands it.

05
Observability is built in, not bolted on

A system you cannot observe is a system you cannot operate. Health probes, structured logging, and deployment audit trails are not optional additions — they are part of the delivery contract. In Kubernetes, liveness and readiness probes are the difference between self-healing infrastructure and a silent failure waiting to be discovered.

The full delivery stack

Every layer of this platform was chosen deliberately. Here is what each one does and why it was chosen over the alternatives.

UserBrowserRoute 53DNSCloudFrontCDN + TLSOACSigV4 authS3Private originTerraform — all resources provisioned as code
DNSAWS Route 53

A alias record routes doraejangue.com to the CloudFront distribution. www redirects to the canonical root domain via a separate S3 redirect bucket and CloudFront distribution.

Route 53 alias records are free and avoid the TTL limitations of CNAME records at the zone apex.
CDN & TLSAWS CloudFront + ACM

CloudFront terminates HTTPS, caches static assets at the edge globally, and enforces redirect-to-HTTPS on all viewer requests. The ACM certificate covers both the root and www domains.

ACM certificates for CloudFront must be issued in us-east-1 — a hard AWS requirement regardless of where your origin lives.
OriginAWS S3 (private)

The S3 bucket stores the Next.js static export. Public access is fully blocked at the bucket level. Only CloudFront can read from it, authenticated via Origin Access Control with SigV4 signing.

OAC (not the legacy OAI) is the AWS-recommended pattern. It uses short-lived signed requests rather than a static identity, which is harder to misuse.
URL routingCloudFront Function

A viewer-request CloudFront Function rewrites clean URLs to their index.html equivalent. /projects/ becomes /projects/index.html before the request hits S3. Without this, direct navigation returns AccessDenied.

A CloudFront Function runs at the edge with sub-millisecond latency and no cold starts — significantly cheaper and faster than a Lambda@Edge for simple URL rewrites.
IaCTerraform

All AWS resources — S3, CloudFront, OAC, ACM, Route 53, IAM OIDC — are provisioned by Terraform. The module structure separates reusable resource definitions from environment-specific configuration.

Terraform state files are never committed to the repository. The .gitignore excludes *.tfstate, *.tfvars, and .terraform/ from the first commit.
CI/CDGitHub Actions + IAM OIDC

Every push to main triggers a CI run. On success, two independent deploy jobs run in parallel: one syncs the static export to S3 and invalidates CloudFront, the other builds and pushes a Docker image to the registry and deploys to Kubernetes.

IAM OIDC eliminates the need to store AWS access keys in GitHub Secrets. GitHub exchanges a short-lived OIDC token for an AWS role — no credentials to rotate, no secrets to leak.

Two tracks, one platform

The same codebase deploys via two independent paths. This is a deliberate architectural choice — not duplication, but demonstration of two distinct delivery patterns.

Primary
Track 1AWS Static
next buildout/ directoryaws s3 syncCloudFront invalidationdoraejangue.com

Optimal for static content. No servers to manage, global CDN, sub-100ms TTFB, effectively zero cost at portfolio scale.

Demonstration
Track 2Kubernetes
docker buildimage registryhelm upgraderolling updateIngress → pods

Demonstrates orchestration, health management, and platform operations — skills that matter for production workloads at scale.

How access is controlled

Every access boundary in this platform is explicit. Nothing is open by default.

S3 fully private

Block Public Access is enabled at the bucket level. No S3 object URL is reachable directly from the internet under any circumstance.

CloudFront-only origin access

The S3 bucket policy allows only the specific CloudFront distribution ARN to call s3:GetObject. Any other principal — including AWS root — is denied.

OAC SigV4 signing

CloudFront authenticates every request to S3 using AWS Signature Version 4. This is the current AWS-recommended pattern, replacing the legacy Origin Access Identity.

Keyless CI/CD auth

GitHub Actions authenticates to AWS using IAM OIDC. Each workflow run receives a short-lived token. No AWS access keys exist in GitHub Secrets.

HTTPS enforced end-to-end

CloudFront is configured with redirect-to-https on all viewer connections. HTTP requests are never served — they are permanently redirected to HTTPS.

Least-privilege IAM

The IAM role assumed by GitHub Actions has only the permissions needed: s3:PutObject, s3:DeleteObject, cloudfront:CreateInvalidation. Nothing more.

See the implementation

Each project page documents the architecture decisions, trade-offs, and lessons learned in full detail.

Cloud Platform →All Projects