<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Aldo’s System Logs]]></title><description><![CDATA[Aldo’s System Logs]]></description><link>https://blog.aldolushkja.it</link><generator>RSS for Node</generator><lastBuildDate>Tue, 09 Jun 2026 02:16:54 GMT</lastBuildDate><atom:link href="https://blog.aldolushkja.it/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Host Your Personal Portfolio on AWS for Almost Nothing — S3, CloudFront & CDK Guide]]></title><description><![CDATA[When I decided to build my personal portfolio at aldolushkja.it, I had two non-negotiable requirements: full infrastructure control and near-zero running costs. No shared hosting, no Netlify/Vercel lo]]></description><link>https://blog.aldolushkja.it/host-your-personal-portfolio-on-aws-for-almost-nothing-s3-cloudfront-cdk-guide</link><guid isPermaLink="true">https://blog.aldolushkja.it/host-your-personal-portfolio-on-aws-for-almost-nothing-s3-cloudfront-cdk-guide</guid><dc:creator><![CDATA[Aldo Lushkja]]></dc:creator><pubDate>Mon, 13 Apr 2026 17:33:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/65d52b5a92a790100fa2c3cd/9c3eaa3e-87b5-4d06-934c-a08ed9cee34d.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When I decided to build my personal portfolio at <a href="https://aldolushkja.it">aldolushkja.it</a>, I had two non-negotiable requirements: full infrastructure control and near-zero running costs. No shared hosting, no Netlify/Vercel lock-in — just AWS primitives managed as code. This post walks through the architecture I ended up with and why each choice makes sense cost-wise.</p>
<hr />
<h2>The Stack at a Glance</h2>
<table>
<thead>
<tr>
<th>Layer</th>
<th>AWS Service</th>
<th>Why</th>
</tr>
</thead>
<tbody><tr>
<td>Storage</td>
<td>S3</td>
<td>Pennies per GB, no compute</td>
</tr>
<tr>
<td>CDN + TLS termination</td>
<td>CloudFront</td>
<td>Generous free tier, global edge</td>
</tr>
<tr>
<td>SSL Certificate</td>
<td>ACM</td>
<td>Free</td>
</tr>
<tr>
<td>DNS</td>
<td>Route 53</td>
<td>$0.50/month per hosted zone</td>
</tr>
<tr>
<td>IaC</td>
<td>AWS CDK (TypeScript)</td>
<td>Repeatable, testable, version-controlled</td>
</tr>
<tr>
<td>CI/CD</td>
<td>GitHub Actions</td>
<td>Free for public repos</td>
</tr>
</tbody></table>
<p>No EC2 instances. No always-on compute. Nothing to patch or resize.</p>
<p><img src="https://mermaid.ink/img/Z3JhcGggTFIKICAgIFVzZXIoKFZpc2l0b3IpKSAtLT4gUjUzW1JvdXRlIDUzXQogICAgUjUzIC0tPiBDRltDbG91ZEZyb250XQogICAgQ0YgLS0+IFMzW1MzIEJ1Y2tldF0KICAgIEFDTVtBQ00gQ2VydGlmaWNhdGVdIC0uIGF0dGFjaGVkIC4tPiBDRgoKICAgIHN0eWxlIFVzZXIgZmlsbDojNEE5MEQ5LGNvbG9yOiNmZmYKICAgIHN0eWxlIENGIGZpbGw6I0ZGOTkwMCxjb2xvcjojZmZmCiAgICBzdHlsZSBTMyBmaWxsOiM1NjlBMzEsY29sb3I6I2ZmZgogICAgc3R5bGUgUjUzIGZpbGw6IzhDNEZGRixjb2xvcjojZmZmCiAgICBzdHlsZSBBQ00gZmlsbDojREQzNDRDLGNvbG9yOiNmZmYK" alt="Architecture Overview" /></p>
<hr />
<h2>Infrastructure as Code with AWS CDK</h2>
<p>Everything lives in a single CDK stack (<code>CdkStack</code>) written in TypeScript. The whole deployment is reproducible from a clean AWS account with one command:</p>
<pre><code class="language-bash">cdk deploy --all --require-approval never
</code></pre>
<p>The stack is parameterized via environment variables, which is what allows the same code to power both production and a staging environment:</p>
<pre><code class="language-typescript">const domainName = process.env.DOMAIN_NAME || 'aldolushkja.it';
const certificateArn = process.env.CERTIFICATE_ARN;

new CdkStack(app, 'RootPortfolioStack', {
  env: { account: CDK_DEFAULT_ACCOUNT, region: CDK_DEFAULT_REGION },
  domainName,
  certificateArn,
});
</code></pre>
<hr />
<h2>S3 — Static File Hosting</h2>
<p>Two S3 buckets handle the two domain variants:</p>
<ul>
<li><code>aldolushkja.it</code> — root domain</li>
<li><code>www.aldolushkja.it</code> — www subdomain</li>
</ul>
<p>Both serve <code>index.html</code> as the index and error document (which is what makes SPA routing work without a server). Bucket versioning is enabled so rollbacks are trivial if a broken deploy goes out.</p>
<pre><code class="language-typescript">const rootBucket = new s3.Bucket(this, 'RootBucket', {
  bucketName: props.domainName,
  websiteIndexDocument: 'index.html',
  websiteErrorDocument: 'index.html',
  versioned: true,
  removalPolicy: cdk.RemovalPolicy.DESTROY,
  autoDeleteObjects: true,
});
</code></pre>
<p><strong>Cost</strong>: a personal portfolio gets maybe a few thousand requests per month and stores well under 1 MB of HTML/CSS/JS. At S3 pricing this is effectively free — we're talking fractions of a cent.</p>
<hr />
<h2>CloudFront — CDN, HTTPS, and Caching</h2>
<p>CloudFront sits in front of S3 and handles three things:</p>
<ol>
<li><strong>HTTPS termination</strong> — the origin is plain S3, CloudFront adds TLS at the edge.</li>
<li><strong>Global caching</strong> — assets are cached at 400+ edge locations, so visitors anywhere in the world get sub-50ms loads.</li>
<li><strong>Custom domain aliases</strong> — both <code>aldolushkja.it</code> and <code>www.aldolushkja.it</code> resolve to the same distribution.</li>
</ol>
<pre><code class="language-typescript">const distribution = new cloudfront.CloudFrontWebDistribution(this, 'SiteDistribution', {
  originConfigs: [{
    s3OriginSource: { s3BucketSource: rootBucket },
    behaviors: [{ isDefaultBehavior: true }],
  }],
  viewerCertificate: cloudfront.ViewerCertificate.fromAcmCertificate(certificate, {
    aliases: [props.domainName, `www.${props.domainName}`],
    securityPolicy: cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021,
    sslMethod: cloudfront.SSLMethod.SNI,
  }),
});
</code></pre>
<p><strong>Cost</strong>: CloudFront's free tier covers 1 TB of data transfer and 10 million HTTP requests per month. A personal portfolio will never get close to that.</p>
<hr />
<h2>ACM — Free SSL Certificates</h2>
<p>ACM certificates are completely free. The only constraint is that certificates used with CloudFront <strong>must be provisioned in <code>us-east-1</code></strong>, regardless of where the rest of your infrastructure lives.</p>
<p>The stack handles this in two ways:</p>
<ul>
<li>If you pass in a pre-existing certificate ARN via env var → it uses that (useful if you already have a wildcard cert).</li>
<li>Otherwise → it creates a <code>DnsValidatedCertificate</code> that auto-validates via Route 53 DNS records.</li>
</ul>
<pre><code class="language-typescript">if (props.certificateArn) {
  certificate = acm.Certificate.fromCertificateArn(this, 'Certificate', props.certificateArn);
} else {
  certificate = new acm.DnsValidatedCertificate(this, 'SiteCertificate', {
    domainName: props.domainName,
    subjectAlternativeNames: [`www.${props.domainName}`],
    hostedZone: zone,
    region: 'us-east-1',
  });
}
</code></pre>
<hr />
<h2>Route 53 — DNS Management</h2>
<p>Route 53 manages the hosted zone for <code>aldolushkja.it</code>. Two <code>A</code> records (alias records) point both the root and www domains to the CloudFront distribution:</p>
<pre><code class="language-typescript">new route53.ARecord(this, 'SiteAliasRecord', {
  recordName: props.domainName,
  target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),
  zone,
});

new route53.ARecord(this, 'WWWSiteAliasRecord', {
  recordName: `www.${props.domainName}`,
  target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),
  zone,
});
</code></pre>
<p>Alias records to CloudFront are free — AWS doesn't charge for queries to alias records that resolve to AWS resources. The only cost is the hosted zone itself: <strong>$0.50/month</strong>.</p>
<hr />
<h2>The Build Pipeline — Docker in CDK</h2>
<p>The frontend build happens inside CDK's bundling step using a Docker container. This means the CI runner doesn't need Node.js pre-installed — CDK pulls a <code>node:22</code> image and builds inside it:</p>
<pre><code class="language-typescript">const frontendAsset = s3Deploy.Source.asset(frontendPath, {
  bundling: {
    image: cdk.DockerImage.fromRegistry('node:22'),
    environment: {
      COMMIT_SHA: commitSha, // injected into index.html at build time
    },
    command: [
      'bash', '-c',
      'npm install &amp;&amp; npm run build:css &amp;&amp; npm run build &amp;&amp; cp -r dist/* /asset-output',
    ],
    user: 'root',
  },
});
</code></pre>
<p>After deployment, CloudFront's cache is automatically invalidated for <code>/*</code> — so visitors always get the latest version without stale edge caches:</p>
<pre><code class="language-typescript">const rootDeployment = new s3Deploy.BucketDeployment(this, 'RootDeployment', {
  destinationBucket: rootBucket,
  sources: [frontendAsset],
  distribution: distribution,
  distributionPaths: ['/*'], // automatic cache bust on every deploy
});
</code></pre>
<p><img src="https://mermaid.ink/img/c2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBHSEEgYXMgR2l0SHViIEFjdGlvbnMKICAgIHBhcnRpY2lwYW50IENESyBhcyBBV1MgQ0RLCiAgICBwYXJ0aWNpcGFudCBEb2NrZXIgYXMgRG9ja2VyCiAgICBwYXJ0aWNpcGFudCBTMyBhcyBTMyBCdWNrZXQKICAgIHBhcnRpY2lwYW50IENGIGFzIENsb3VkRnJvbnQKCiAgICBHSEEtPj5DREs6IGNkayBkZXBsb3kKICAgIENESy0+PkRvY2tlcjogU3BpbiB1cCBjb250YWluZXIKICAgIERvY2tlci0+PkRvY2tlcjogbnBtIGluc3RhbGwKICAgIERvY2tlci0+PkRvY2tlcjogbnBtIHJ1biBidWlsZAogICAgRG9ja2VyLT4+Q0RLOiBkaXN0IG91dHB1dAogICAgQ0RLLT4+UzM6IFVwbG9hZCBhc3NldHMKICAgIENESy0+PkNGOiBJbnZhbGlkYXRlIGNhY2hlCiAgICBDRi0tPj5HSEE6IERlcGxveW1lbnQgY29tcGxldGUK" alt="Build Pipeline" /></p>
<hr />
<h2>Two Environments, One Codebase</h2>
<p>I maintain two environments: production (<code>aldolushkja.it</code>) and staging (<code>dev.aldolushkja.it</code>). Both are driven by the same CDK stack — the only difference is the <code>DOMAIN_NAME</code> environment variable.</p>
<p><img src="https://mermaid.ink/img/Z3JhcGggVEQKICAgIFB1c2hbR2l0IFB1c2hdIC0tPnxtYXN0ZXJ8IEdIQV9Qcm9kW0dBOiBjZGstZGVwbG95LnltbF0KICAgIFB1c2ggLS0+fGRldnwgR0hBX0RldltHQTogY2RrLWRlcGxveS1kZXYueW1sXQoKICAgIEdIQV9Qcm9kIC0tPiBCdWlsZF9Qcm9kW0J1aWxkIENESyArIFRlc3RzXQogICAgR0hBX0RldiAtLT4gQnVpbGRfRGV2W0J1aWxkIENESyArIFRlc3RzXQoKICAgIEJ1aWxkX1Byb2QgLS0+IERlcGxveV9Qcm9kW0RlcGxveSB0byBhbGRvbHVzaGtqYS5pdF0KICAgIEJ1aWxkX0RldiAtLT4gRGVwbG95X0RldltEZXBsb3kgdG8gZGV2LmFsZG9sdXNoa2phLml0XQoKICAgIERlcGxveV9Qcm9kIC0tPiBQcm9kW1Byb2R1Y3Rpb25dCiAgICBEZXBsb3lfRGV2IC0tPiBEZXZbU3RhZ2luZ10KCiAgICBzdHlsZSBQcm9kIGZpbGw6IzU2OUEzMSxjb2xvcjojZmZmCiAgICBzdHlsZSBEZXYgZmlsbDojRkY5OTAwLGNvbG9yOiNmZmYK" alt="CI/CD Pipeline" /></p>
<p><strong><code>cdk-deploy.yml</code></strong> triggers on push to <code>master</code> — no <code>DOMAIN_NAME</code> set, so it defaults to <code>aldolushkja.it</code>.</p>
<p><strong><code>cdk-deploy-dev.yml</code></strong> triggers on push to <code>dev</code>:</p>
<pre><code class="language-yaml">env:
  DOMAIN_NAME: dev.aldolushkja.it
</code></pre>
<p>This means I can test infrastructure changes on the dev subdomain before they hit production — without any extra tooling or duplication.</p>
<hr />
<h2>What's the Actual Monthly Bill?</h2>
<p>For a personal portfolio with moderate traffic:</p>
<table>
<thead>
<tr>
<th>Service</th>
<th>Cost</th>
</tr>
</thead>
<tbody><tr>
<td>S3 (storage + requests)</td>
<td>~$0.01</td>
</tr>
<tr>
<td>CloudFront</td>
<td>$0.00 (free tier)</td>
</tr>
<tr>
<td>ACM</td>
<td>$0.00</td>
</tr>
<tr>
<td>Route 53 (hosted zone)</td>
<td>$0.50</td>
</tr>
<tr>
<td><strong>Total</strong></td>
<td><strong>~$0.51/month</strong></td>
</tr>
</tbody></table>
<p>That's it. The only real cost is the Route 53 hosted zone. Everything else fits comfortably within free tiers or rounds to zero at personal-site traffic levels.</p>
<hr />
<h2>What I'd Add Next</h2>
<p>The repo already has a <code>BackendStack</code> defined (but not deployed) for a Lambda + API Gateway contact form. When I activate it, Lambda's free tier (1M requests/month) means it'll still cost essentially nothing.</p>
<hr />
<h2>Takeaways</h2>
<ul>
<li><strong>Static sites on S3 + CloudFront are the most cost-efficient way to host anything that doesn't need a server.</strong> The combination gives you global CDN, HTTPS, and high availability for pocket change.</li>
<li><strong>AWS CDK makes this reproducible and testable.</strong> The entire infrastructure is TypeScript, sits in the same repo as the frontend, and has unit tests.</li>
<li><strong>Parameterizing the stack for multiple environments costs nothing extra</strong> — it's just the same S3/CloudFront/Route53 setup pointed at a different domain.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[I Built a Zero-Framework MCP Server for Targetprocess in Java 21]]></title><description><![CDATA[Every morning I open Targetprocess, navigate to my board, filter by assignee, check sprint progress, and look for blockers. Then I open my AI assistant to plan the day — and immediately have to descri]]></description><link>https://blog.aldolushkja.it/i-built-a-zero-framework-mcp-server-for-targetprocess-in-java-21</link><guid isPermaLink="true">https://blog.aldolushkja.it/i-built-a-zero-framework-mcp-server-for-targetprocess-in-java-21</guid><dc:creator><![CDATA[Aldo Lushkja]]></dc:creator><pubDate>Fri, 27 Mar 2026 21:40:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/65d52b5a92a790100fa2c3cd/739f546c-5256-4508-923a-1d952b3275d7.svg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every morning I open Targetprocess, navigate to my board, filter by assignee, check sprint progress, and look for blockers. Then I open my AI assistant to plan the day — and immediately have to describe everything I just saw, manually, in a prompt.</p>
<p>That's the gap I wanted to close.</p>
<h2>What is MCP and why does it matter?</h2>
<p><a href="https://modelcontextprotocol.io">Model Context Protocol (MCP)</a> is an open standard that lets AI assistants call external tools directly. Instead of you describing your data to the AI, the AI fetches it itself. It's the difference between <em>"I have 14 open bugs, here's a summary..."</em> and just asking <em>"What are my open bugs?"</em> and getting the answer.</p>
<p>If your team uses Targetprocess, you interact with it dozens of times a day — checking user stories, creating tasks, linking blockers, updating statuses. All of that is now something your AI assistant can do for you.</p>
<h2>The result: zdtp-mcp</h2>
<p><a href="https://github.com/aldo-lushkja/zdtp-mcp">zdtp-mcp</a> is an MCP server that exposes <strong>52 tools across 15 Targetprocess domains</strong>:</p>
<table>
<thead>
<tr>
<th>Domain</th>
<th>What you can do</th>
</tr>
</thead>
<tbody><tr>
<td>User Stories</td>
<td>search, create, update, get, delete</td>
</tr>
<tr>
<td>Tasks</td>
<td>search, create, update, get, delete</td>
</tr>
<tr>
<td>Bugs</td>
<td>search, create, update, get, delete</td>
</tr>
<tr>
<td>Test Plans &amp; Cases</td>
<td>full CRUD + inline step creation</td>
</tr>
<tr>
<td>Epics, Features, Releases</td>
<td>full CRUD</td>
</tr>
<tr>
<td>Relations</td>
<td>search, link, delete</td>
</tr>
<tr>
<td>Teams, Sprints, Projects, Users</td>
<td>search &amp; get</td>
</tr>
<tr>
<td>Comments</td>
<td>add to any entity</td>
</tr>
</tbody></table>
<p>One Docker command to set it up:</p>
<pre><code class="language-bash"># Claude Code
claude mcp add zdtp -- docker run -i --rm \
  -e TP_URL="https://youraccount.tpondemand.com" \
  -e TP_TOKEN="your_token" \
  ghcr.io/aldo-lushkja/zdtp-mcp:latest

# Gemini CLI
gemini mcp add zdtp docker run -i --rm \
  -e TP_URL="https://youraccount.tpondemand.com" \
  -e TP_TOKEN="your_token" \
  ghcr.io/aldo-lushkja/zdtp-mcp:latest
</code></pre>
<p>Then you can just ask:</p>
<blockquote>
<p><em>"Find all open bugs assigned to me in Project Alpha"</em></p>
<p><em>"Link US-123 as a blocker of US-456"</em></p>
<p><em>"Create a test case with steps for the payment flow"</em></p>
<p><em>"Show me all releases due this month"</em></p>
</blockquote>
<h2>Why Java 21 and zero dependencies?</h2>
<p>Most MCP servers I've seen are built with Python or TypeScript, often with a framework doing heavy lifting. I chose Java 21 with a deliberate <strong>zero-framework policy</strong>: no Spring, no Quarkus, no HTTP framework of any kind.</p>
<p>The reasons:</p>
<p><strong>Startup time.</strong> MCP servers over stdio are spawned on demand — every time the AI client needs them. A Spring Boot service takes a few seconds to start. A plain Java process with a shadow JAR is up in under 100ms.</p>
<p><strong>Size.</strong> The final fat JAR is small. No framework means no classpath scanning, no annotation processing, no auto-configuration. The Docker image stays lean.</p>
<p><strong>Predictability.</strong> When something breaks, you read the code — not a framework's internals. For a small, focused tool this matters a lot.</p>
<p>The only dependencies are Jackson (JSON) and Commonmark (Markdown-to-HTML for description fields). Everything else — HTTP client, stdio loop, JSON-RPC parsing — is standard Java 21.</p>
<h2>Architecture: BCE</h2>
<p>The project follows the <strong>Boundary-Control-Entity (BCE)</strong> pattern:</p>
<pre><code class="language-plaintext">McpServer (stdio JSON-RPC loop)
    └── *McpTools        ← Boundary: registers tools, formats output
        └── *Service     ← Control: business logic, calls QueryEngine
            └── QueryEngine → Targetprocess REST API
</code></pre>
<p>Each Targetprocess domain (UserStory, Bug, Task, etc.) is a vertical slice with its own boundary, control, and entity classes. Adding a new domain is a matter of copying the pattern — no framework wiring, no configuration files.</p>
<p>The <code>McpServer</code> class itself is ~140 lines. It reads JSON-RPC from stdin, dispatches to registered tool handlers, and writes responses to stdout. That's the entire transport layer.</p>
<h2>Multi-platform Docker image</h2>
<p>The published image targets both <code>linux/amd64</code> and <code>linux/arm64</code>, so it runs natively on Apple Silicon without Rosetta emulation:</p>
<pre><code class="language-plaintext">ghcr.io/aldo-lushkja/zdtp-mcp:latest   # stable
ghcr.io/aldo-lushkja/zdtp-mcp:develop  # latest dev build
</code></pre>
<p>The CI/CD pipeline is a full Git Flow setup on GitHub Actions: feature branches merge into <code>develop</code>, release branches cut from <code>develop</code> and merge into <code>main</code>, Docker images are tagged automatically per branch.</p>
<h2>What's next</h2>
<p>The server currently supports stdio transport only. HTTP/SSE transport would allow deploying it as a shared team service — one running instance, multiple AI clients pointing at it. That's the next milestone.</p>
<p>If your team uses Targetprocess and AI assistants, <a href="https://github.com/aldo-lushkja/zdtp-mcp">give it a try</a>. PRs and issues welcome.</p>
]]></content:encoded></item><item><title><![CDATA[Boost Your Local Development with Docker & Docker Compose]]></title><description><![CDATA[🚀 Supercharging Your Local Development with Docker and Docker Compose
As a software engineer who's spent years optimizing development workflows, I can confidently say that Docker and Docker Compose have revolutionized how we set up and manage local ...]]></description><link>https://blog.aldolushkja.it/boost-your-local-development-with-docker-docker-compose</link><guid isPermaLink="true">https://blog.aldolushkja.it/boost-your-local-development-with-docker-docker-compose</guid><category><![CDATA[Docker]]></category><category><![CDATA[Docker compose]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Infrastructure as code]]></category><dc:creator><![CDATA[Aldo Lushkja]]></dc:creator><pubDate>Fri, 07 Feb 2025 23:28:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/06y6wukkSKg/upload/bab3cdf3751419ea00223a9eff447793.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-supercharging-your-local-development-with-docker-and-docker-compose">🚀 Supercharging Your Local Development with Docker and Docker Compose</h1>
<p>As a software engineer who's spent years optimizing development workflows, I can confidently say that Docker and Docker Compose have revolutionized how we set up and manage local development environments. In this article, I'll share practical insights on using these tools to build robust infrastructure and accelerate your development process.</p>
<h2 id="heading-why-docker-for-local-development">🤌🏻 Why Docker for Local Development?</h2>
<p>Before diving into the how-to's, let's address a fundamental question: Why should you use Docker for local development? Here are the compelling reasons:</p>
<ol>
<li><p><strong>Consistency</strong>: The age-old "it works on my machine" problem becomes a thing of the past</p>
</li>
<li><p><strong>Isolation</strong>: Each project runs in its own container, preventing dependency conflicts</p>
</li>
<li><p><strong>Reproducibility</strong>: New team members can start contributing in minutes, not days</p>
</li>
<li><p><strong>Scalability</strong>: Easily add new services as your application grows</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738970501924/8d6e1555-470c-4262-80a5-5d5cad0561b9.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-getting-started-with-docker">⚙️Getting Started with Docker</h2>
<p>To begin your Docker journey, you'll need to install Docker Desktop (for Windows/Mac) or Docker Engine (for Linux). Once installed, you can create your first Dockerfile:</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># Use an official Node.js runtime as the base image</span>
<span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>-alpine

<span class="hljs-comment"># Set the working directory</span>
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>

<span class="hljs-comment"># Copy package files</span>
<span class="hljs-keyword">COPY</span><span class="bash"> package*.json ./</span>

<span class="hljs-comment"># Install dependencies</span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm install</span>

<span class="hljs-comment"># Copy the rest of the application</span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>

<span class="hljs-comment"># Expose port 3000</span>
<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">3000</span>

<span class="hljs-comment"># Start the application</span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"npm"</span>, <span class="hljs-string">"start"</span>]</span>
</code></pre>
<h2 id="heading-enter-docker-compose-managing-multi-container-applications">🚄 Enter Docker Compose: Managing Multi-Container Applications</h2>
<p>While Docker is great for single containers, real-world applications often require multiple services working together. This is where Docker Compose shines. Here's a practical example:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">'3.8'</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-attr">web:</span>
    <span class="hljs-attr">build:</span> <span class="hljs-string">.</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"3000:3000"</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">.:/app</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/app/node_modules</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">NODE_ENV=development</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">db</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">redis</span>

  <span class="hljs-attr">db:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">postgres:14-alpine</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">postgres_data:/var/lib/postgresql/data</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">POSTGRES_USER=myapp</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">POSTGRES_PASSWORD=mypassword</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">POSTGRES_DB=myapp_development</span>

  <span class="hljs-attr">redis:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">redis:alpine</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"6379:6379"</span>

<span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">postgres_data:</span>
</code></pre>
<h2 id="heading-best-practices-for-local-development">📋Best Practices for Local Development</h2>
<h3 id="heading-1-use-volume-mounting">1. Use Volume Mounting</h3>
<p>Instead of rebuilding containers for every code change, use volume mounting to reflect changes instantly:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">volumes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">.:/app</span>  <span class="hljs-comment"># Mount current directory to /app in container</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">/app/node_modules</span>  <span class="hljs-comment"># Preserve container's node_modules</span>
</code></pre>
<h3 id="heading-2-environment-management">2. Environment Management</h3>
<p>Create separate <code>.env</code> files for different environments:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># .env.development</span>
DB_HOST=db
REDIS_URL=redis://redis:6379
NODE_ENV=development
</code></pre>
<h3 id="heading-3-development-specific-optimizations">3. Development-Specific Optimizations</h3>
<p>For local development, consider these additions to your <code>docker-compose.yml</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">services:</span>
  <span class="hljs-attr">web:</span>
    <span class="hljs-comment"># ... other configurations ...</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">dev</span>  <span class="hljs-comment"># Use development server</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"9229:9229"</span>  <span class="hljs-comment"># Enable debugging</span>
</code></pre>
<h2 id="heading-common-development-workflows">🌊 Common Development Workflows</h2>
<h3 id="heading-starting-your-development-environment">Starting Your Development Environment</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Start all services</span>
docker-compose up -d

<span class="hljs-comment"># View logs</span>
docker-compose logs -f web

<span class="hljs-comment"># Stop all services</span>
docker-compose down
</code></pre>
<h3 id="heading-running-tests">Running Tests</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Run tests in the web service</span>
docker-compose <span class="hljs-built_in">exec</span> web npm <span class="hljs-built_in">test</span>

<span class="hljs-comment"># Run database migrations</span>
docker-compose <span class="hljs-built_in">exec</span> web npm run migrate
</code></pre>
<h2 id="heading-advanced-tips-and-tricks">🛠️ Advanced Tips and Tricks</h2>
<h3 id="heading-1-docker-compose-override-files">1. Docker Compose Override Files</h3>
<p>Use <code>docker-compose.override.yml</code> for development-specific settings:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># docker-compose.override.yml</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-attr">web:</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">dev</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">.:/app</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">DEBUG=true</span>
</code></pre>
<h3 id="heading-2-multi-stage-builds">2. Multi-Stage Builds</h3>
<p>Optimize your production builds while keeping development containers lean:</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># Development stage</span>
<span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>-alpine AS development
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-keyword">COPY</span><span class="bash"> package*.json ./</span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm install</span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>

<span class="hljs-comment"># Production stage</span>
<span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>-alpine AS production
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-keyword">COPY</span><span class="bash"> package*.json ./</span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm ci --only=production</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=development /app/dist ./dist</span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"npm"</span>, <span class="hljs-string">"start"</span>]</span>
</code></pre>
<h2 id="heading-common-pitfalls-and-solutions">🔨 Common Pitfalls and Solutions</h2>
<ol>
<li><p><strong>Container Performance Issues</strong></p>
<ul>
<li><p>Use <code>.dockerignore</code> to exclude unnecessary files</p>
</li>
<li><p>Implement proper volume mounting strategies</p>
</li>
<li><p>Consider using Docker Desktop's resource limits</p>
</li>
</ul>
</li>
<li><p><strong>Database Persistence</strong></p>
<ul>
<li><p>Always use named volumes for databases</p>
</li>
<li><p>Implement proper backup strategies</p>
</li>
<li><p>Use volume mounting for development data</p>
</li>
</ul>
</li>
<li><p><strong>Network Issues</strong></p>
<ul>
<li><p>Use service names as hostnames</p>
</li>
<li><p>Properly manage port mappings</p>
</li>
<li><p>Understand Docker's networking models</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-conclusion">👋🏻 Conclusion</h2>
<p>Docker and Docker Compose are powerful tools that can significantly improve your local development workflow. By following these practices and understanding the underlying concepts, you can create a development environment that's both efficient and enjoyable to work with.</p>
<p>Remember: The goal is to spend less time fighting with environment setup and more time writing great code. Docker helps achieve exactly that.</p>
<h2 id="heading-additional-resources">📚 Additional Resources</h2>
<ul>
<li><p><a target="_blank" href="https://docs.docker.com/">Official Docker Documentation</a></p>
</li>
<li><p><a target="_blank" href="https://docs.docker.com/compose/">Docker Compose Documentation</a></p>
</li>
<li><p><a target="_blank" href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices/">Docker Best Practices</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Empowering Asynchronous Communication with Netty and Reactive HTTP Client]]></title><description><![CDATA[📖 Introduction
In the dynamic landscape of web development, the need for efficient, scalable, and asynchronous communication has become paramount. Netty, a robust and high-performance networking framework, coupled with a reactive HTTP client, offers...]]></description><link>https://blog.aldolushkja.it/empowering-asynchronous-communication-with-netty-and-reactive-http-client</link><guid isPermaLink="true">https://blog.aldolushkja.it/empowering-asynchronous-communication-with-netty-and-reactive-http-client</guid><category><![CDATA[Java]]></category><category><![CDATA[Reactor]]></category><category><![CDATA[Reactive Programming]]></category><category><![CDATA[Non-Blocking]]></category><category><![CDATA[Performance Optimization]]></category><dc:creator><![CDATA[Aldo Lushkja]]></dc:creator><pubDate>Wed, 21 Feb 2024 00:30:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/rIC-q1ds6dM/upload/50467d0e3c92c5dbcd75c375b7b05022.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">📖 Introduction</h2>
<p>In the dynamic landscape of web development, the need for efficient, scalable, and asynchronous communication has become paramount. <strong>Netty</strong>, a robust and high-performance networking framework, coupled with a <strong>reactive HTTP client</strong>, offers a powerful solution to meet these demands. In this article, we will explore the fundamentals of Netty and delve into the intricacies of sending requests using a reactive HTTP client.</p>
<h2 id="heading-understanding-netty">💡 Understanding Netty</h2>
<p><strong>Netty</strong>, an open-source framework, excels in building high-performance and scalable network applications. Its asynchronous event-driven architecture is well-suited for handling a large number of connections simultaneously, making it a popular choice for building servers and clients in various domains.</p>
<h3 id="heading-key-features-of-netty">⭐ Key Features of Netty</h3>
<ol>
<li><p><strong>Event-Driven Model</strong>: Netty utilizes an event-driven programming model, allowing developers to handle I/O operations asynchronously. This helps in achieving high concurrency and responsiveness in applications.</p>
</li>
<li><p><strong>Channel Abstraction</strong>: Netty's channel abstraction simplifies network communication by providing a consistent interface for various transport types, such as sockets or Datagram channels.</p>
</li>
<li><p><strong>Thread Pools</strong>: Netty efficiently manages threads through its customizable thread pools, optimizing resource utilization and ensuring smooth operation in high-throughput scenarios.</p>
</li>
</ol>
<h2 id="heading-reactive-http-client">🚀 Reactive HTTP Client</h2>
<p>To enhance the capabilities of Netty, developers often integrate a reactive HTTP client. Reactive programming is an approach that focuses on handling asynchronous data streams and the propagation of change. When applied to HTTP communication, it provides a non-blocking and event-driven paradigm, aligning well with Netty's principles.</p>
<h3 id="heading-advantages-of-reactive-http-client-with-netty">Advantages of Reactive HTTP Client with Netty</h3>
<ol>
<li><p><strong>Asynchronous Communication</strong>: Reactive HTTP clients, such as WebClient in the Spring WebFlux framework or Project Reactor's <code>Mono</code> and <code>Flux</code>, allow developers to perform HTTP requests asynchronously. This is particularly beneficial in scenarios where responsiveness and scalability are critical.</p>
</li>
<li><p><strong>Streamlined Error Handling</strong>: Reactive programming facilitates concise error handling through features like reactive streams and onError. This makes it easier to manage errors and respond appropriately in asynchronous environments.</p>
</li>
<li><p><strong>Backpressure Support</strong>: Reactive HTTP clients often come with built-in support for backpressure, allowing applications to manage the flow of data efficiently. This is crucial for preventing overload in scenarios where the producer is faster than the consumer.</p>
</li>
</ol>
<h2 id="heading-sample-code-snippet-sending-request-with-netty-and-reactive-http-client">💻 Sample Code Snippet - Sending Request with Netty and Reactive HTTP Client</h2>
<pre><code><span class="hljs-keyword">import</span> reactor.core.publisher.Mono;
<span class="hljs-keyword">import</span> org.springframework.web.reactive.function.client.WebClient;

public <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ReactiveHttpClientExample</span> </span>{
    private final WebClient webClient;

    public ReactiveHttpClientExample() {
        <span class="hljs-built_in">this</span>.webClient = WebClient.builder()
                .baseUrl(<span class="hljs-string">"http://example.com"</span>)
                .build();
    }

    public Mono&lt;<span class="hljs-built_in">String</span>&gt; sendRequest() {
        <span class="hljs-keyword">return</span> webClient.get()
                .uri(<span class="hljs-string">"/api/data"</span>)
                .retrieve()
                .bodyToMono(<span class="hljs-built_in">String</span>.class)
                .doOnError(error -&gt; System.err.println(<span class="hljs-string">"Error occurred: "</span> + error.getMessage()));
    }
}
</code></pre><h2 id="heading-conclusion">👋🏻 Conclusion</h2>
<p>In the ever-evolving landscape of web development, <strong>Netty</strong> and reactive HTTP clients provide a robust foundation for building high-performance, asynchronous, and scalable applications. Developers can leverage the strengths of both technologies to create responsive systems capable of handling a large number of concurrent connections. Asynchronous communication, streamlined error handling, and backpressure support make the combination of Netty and reactive HTTP clients a compelling choice for modern, data-intensive applications.</p>
]]></content:encoded></item></channel></rss>