<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>php &#8211; Rafael Bernard Araujo</title>
	<atom:link href="https://rafael.bernard-araujo.com/tag/php/feed" rel="self" type="application/rss+xml" />
	<link>https://rafael.bernard-araujo.com</link>
	<description>desenvolvendo... while(!success){  try(); }</description>
	<lastBuildDate>Sun, 20 Sep 2026 22:29:32 +0000</lastBuildDate>
	<language>pt-BR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
<site xmlns="com-wordpress:feed-additions:1">21941730</site>	<item>
		<title>The Principle Is Not the Mechanism: ISP, DIP, and Language Structures in Practice</title>
		<link>https://rafael.bernard-araujo.com/the-principle-is-not-the-mechanism-isp-dip-and-language-structures-in-practice.php</link>
					<comments>https://rafael.bernard-araujo.com/the-principle-is-not-the-mechanism-isp-dip-and-language-structures-in-practice.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Wed, 09 Sep 2026 05:54:06 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[dependency-injection]]></category>
		<category><![CDATA[design principles]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[software engineering]]></category>
		<category><![CDATA[SOLID]]></category>
		<category><![CDATA[structural-typing]]></category>
		<category><![CDATA[typescript]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2420</guid>

					<description><![CDATA[A small refactor made me think: how many times do we mix a principle with mechanisms? For example, Interface Segregation and Dependency Inversion are many times seen naturally in OOP, but they are principles and they survive a change of paradigm. TypeScript is this interesting case: it supports both OOP and functional styles in the [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><em>A small refactor made me think: how many times do we mix a principle with mechanisms? For example, Interface Segregation and Dependency Inversion are many times seen naturally in OOP, but they are principles and they survive a change of paradigm. TypeScript is this interesting case: it supports both OOP and functional styles in the same codebase, and both can preserve the I and D from SOLID. This article separates the principles from the mechanisms that carry them, maps how OOP and functional TypeScript preserve the same design responsibilities. And it proposes yet another comparison of things that look tied together, but are separate concepts in the language structures underneath: where structural typing and duck typing share a philosophy but diverge in practice.</em></p>
<h2>Contents</h2>
<ol>
<li>The in-memory repository that made the mapping visible</li>
<li>Preserve the responsibilities, change the mechanisms</li>
<li>Map responsibilities, not syntax</li>
<li>Service factories, closures, and composition</li>
<li>Structural typing: the conformance mechanism</li>
<li>Testing: where duck typing walks back in</li>
<li>Untangling the five concepts</li>
<li>The language field guide</li>
<li>A good way to use them</li>
<li>The honest counterpoint</li>
<li>Checklist</li>
<li>Recap</li>
</ol>
<p><em>Obs.: Check the Glossary at the end if you need clarification on concepts I am using in the article.</em></p>
<h2>The in-memory repository that made the mapping visible</h2>
<p>In a production TypeScript API I work on, the persistence design was still evolving while the Use Cases needed to move forward. We deliberately introduced an in-memory implementation of the domain contract:</p>
<pre><code class="language-typescript">// infrastructure/repository/in-memory.user.repository.ts
import { randomUUID } from &#039;node:crypto&#039;;
import type { NewUser, User, UserRepository } from &#039;../../domain/user&#039;;

const usersByDocument = new Map&lt;string, User&gt;();

async function findByDocument(document: string): Promise&lt;User | null&gt; {
  return usersByDocument.get(document) ?? null;
}

async function create(data: NewUser): Promise&lt;User&gt; {
  const user: User = { id: randomUUID(), ...data, createdAt: new Date() };
  usersByDocument.set(data.document, user);
  return user;
}

export const inMemoryUserRepository: UserRepository = { findByDocument, create };</code></pre>
<p>The services that depended on <code>UserRepository</code> received it as a parameter:</p>
<pre><code class="language-typescript">export async function register(
  input: RegisterInput,
  users: UserRepository,
): Promise&lt;User&gt; {
  const existing = await users.findByDocument(input.document);
  // ...
  return users.create(input);
}</code></pre>
<p>At the call site, the temporary seam was explicit:</p>
<pre><code class="language-typescript">await register(input, inMemoryUserRepository);</code></pre>
<p>That parameter was deliberate scaffolding. It allowed the Use Cases to progress against the contract while making the temporary in-memory choice visible wherever the service was called.</p>
<p>Later, the Postgres implementation of <code>UserRepository</code> became available. We began removing the extra repository parameter and moving toward an agreed convention: import the repository and use it inside the service. That convention itself would become part of the question.</p>
<pre><code class="language-ts">import { postgresUserRepository } from &#039;../infrastructure/repository/postgres.user.repository&#039;;

export async function register(input: RegisterInput): Promise&lt;User&gt; {
  const existing = await postgresUserRepository.findByDocument(input.document);
  // ...
  return postgresUserRepository.create(input);
}</code></pre>
<p>That was the direction of the refactor, not the conclusion of the design. It removed the temporary parameter and restored consistency with the surrounding code, but it also moved the implementation choice directly into the service and removed the visible substitution boundary. In PHP, I knew exactly where each responsibility lived. In TypeScript, the functional style I was working in didn't use those mechanisms.</p>
<p>In the OOP style, each responsibility had an obvious home: a small interface supported Interface Segregation; constructor injection supported Dependency Inversion; a Service Container selected implementations and assembled the graph; tests supplied another implementation of the same interface. The direct-import style looked different enough to raise the real question:</p>
<blockquote>
<p>Am I giving up Interface Segregation, Dependency Inversion, centralised composition, or isolated testing? Is functional TypeScript unsuitable for these principles, or am I looking for their OOP mechanisms instead of their functional equivalents? Should this service become a class, or can the functional style preserve the same design qualities from implementation through testing?</p>
</blockquote>
<p>The refactor did not reveal a missing principle. It revealed a comparison worth making: for every responsibility that had an obvious home in the OOP style, what technique (and what language structure) gave it an equivalent home in the functional style?</p>
<p>That comparison turned out to be a useful exercise in itself. Not just which pattern to use, but what is a principle, what is a mechanism, and what is a language structure. Where each of those pieces actually plays its role. The rest of this article works through that distinction, one responsibility at a time.</p>
<h2>Preserve the responsibilities, change the mechanisms</h2>
<p>The comparison becomes clearer when the creation of one service is separated from the composition of the whole application:</p>
<table>
<thead>
<tr>
<th>Design responsibility</th>
<th>Common OOP mechanism</th>
<th>Functional TypeScript mechanism</th>
</tr>
</thead>
<tbody>
<tr>
<td>Keep the consumer contract narrow</td>
<td>A small, consumer-specific interface</td>
<td>A small structural contract</td>
</tr>
<tr>
<td>Create one service instance</td>
<td>Invoke a class constructor directly or through a dedicated factory</td>
<td>Invoke a service factory such as <code>makeRegistrationService(...)</code></td>
</tr>
<tr>
<td>Retain injected dependencies</td>
<td>Private object fields</td>
<td>The returned functions' lexical closures</td>
</tr>
<tr>
<td>Compose the application graph</td>
<td>Invoke constructors/factories manually at the composition root, or use a Service Container</td>
<td>Invoke service factories manually at the composition root, or use a Service Container</td>
</tr>
<tr>
<td>Expose the wired application</td>
<td>The entry point receives or resolves a root service/application object</td>
<td>The composition function returns an application facade with already-wired operations</td>
</tr>
<tr>
<td>Test a consumer in isolation</td>
<td>Inject a fake/mock implementation through the constructor</td>
<td>Pass a structural or duck-typed double to the service factory</td>
</tr>
</tbody>
</table>
<p>This makes the factory equivalence explicit. <code>makeRegistrationService(users)</code> is doing two familiar jobs at once: its parameters are the functional counterpart of constructor injection, and invoking it is the functional counterpart of constructing the service through <code>new</code> or through a small OOP factory. The returned closure or object of functions is the service instance.</p>
<p>It also keeps <strong>composition</strong>, <strong>composition root</strong>, and <strong>Service Container</strong> distinct:</p>
<ul>
<li><strong>Composition</strong> is the activity of assembling the graph.</li>
<li>The <strong>composition root</strong> is the application location where that activity happens.</li>
<li>A <strong>Service Container</strong> is one optional mechanism for performing composition and resolution, often with lifecycle, scope, or discovery features. It can be used in either an OOP or functional application.</li>
<li>An explicit composition function is manual composition, not a complete replacement for every container capability.</li>
</ul>
<p>The composed result is another role again. The <code>application.register</code> operation on the returned <strong>application facade</strong> is a ready-to-use entry point into the wired graph. It is not a Service Container because callers are not resolving arbitrary services by token, and it is not managing service lifecycles. If application code asks a container for dependencies directly, that moves toward the Service Locator pattern rather than dependency injection.</p>
<p>None of these mechanisms creates a design principle by itself. A large interface still violates ISP. A constructor or factory typed to a concrete class still violates DIP. A container or composition function can centralise poor dependencies as efficiently as good ones. The value comes from assigning each responsibility deliberately.</p>
<p>Import/export syntax is not part of the comparison; both styles use ordinary module boundaries. The meaningful question is <strong>where the concrete implementation is selected</strong>. In the opening refactor, the Use Case selects <code>postgresUserRepository</code> itself and hard-wires the detail. In the mapped design, the composition root selects it and the service factory receives only the <code>UserRepository</code> contract.</p>
<p>The discovery wasn't any of the individual concepts, they were all familiar. It was the <strong>mapping between coding styles</strong>. How do OOP and functional TypeScript use different techniques and language structures to preserve Interface Segregation and Dependency Inversion, from implementation through isolated testing? Asking that comparative question put contracts, constructors, factories, closures, composition, containers, facades, and test doubles in their respective roles. The concepts stayed the same; the mechanisms changed.</p>
<p>That comparison opened a second layer: <strong>what structure in the language makes each technique possible?</strong> Class constructors and object fields create and retain dependencies in the OOP route. First-class functions and lexical closures do the equivalent work in the functional route, while ordinary function application composes the graph. TypeScript's structural type system checks contracts without nominal declarations. JavaScript's dynamic dispatch allows duck-typed doubles at runtime, while TypeScript can check the same doubles structurally before tests run. The organizing chain is therefore not only <em>principle → technique</em>, but <strong>design responsibility → technique → enabling language structure</strong>.</p>
<h2>Map responsibilities, not syntax</h2>
<p>Dependency Inversion and dependency injection are related, but they are not the same thing:</p>
<ul>
<li><strong>Dependency Inversion</strong> (the D in SOLID) is a principle about <strong>direction</strong>: high-level policy must not depend on low-level detail; both depend on abstractions.</li>
<li><strong>Dependency injection</strong> is a technique that serves it: bind a concrete dependency outside the code that consumes it.</li>
</ul>
<p>The OOP version stores an injected dependency in an object:</p>
<pre><code class="language-typescript">class RegistrationService {
  constructor(private readonly users: UserRepository) {}

  async register(input: RegisterInput) {
    const existing = await this.users.findByDocument(input.document);
    // ...
  }
}</code></pre>
<p>The functional version stores the same dependency in a closure:</p>
<pre><code class="language-typescript">function makeRegistrationService(users: UserRepository) {
  return {
    async register(input: RegisterInput) {
      const existing = await users.findByDocument(input.document);
      // ...
    },
  };
}</code></pre>
<p>Both consumers depend on <code>UserRepository</code>, not on a database implementation. Both receive the concrete dependency from somewhere else. The difference is where the language stores the provided value: an object field in one case, a lexical environment in the other.</p>
<p>Composition completes the mapping:</p>
<pre><code class="language-typescript">// composition/application.ts
import { postgresUserRepository } from &#039;../infrastructure/repository/postgres.user.repository&#039;;

function createRegistrationApplication(users: UserRepository) {
  const registration = makeRegistrationService(users);
  return { register: registration.register };
}

const application = createRegistrationApplication(postgresUserRepository);

await application.register(input);</code></pre>
<p>The composition root chooses the Postgres implementation, injects it once, and receives an application facade. Calling <code>application.register</code> does not choose, construct, or locate a repository. In a container-based application, a Service Container may perform this composition at the root. Here the composition function performs it manually. Neither the composition function nor the returned facade is itself a Service Container.</p>
<p>This is also where Interface Segregation fits. <code>UserRepository</code> should describe only what the registration Use Case needs. Structural typing does not guarantee ISP (nothing prevents us from declaring a fat contract), but it removes much of the implementation cost of keeping contracts small. Any value with the required shape can satisfy the consumer's contract without joining an inheritance hierarchy or declaring <code>implements</code>. (More details about <code>implements</code> down in the article.)</p>
<h2>Service factories, closures, and composition</h2>
<p>Three elements do different work here: the <strong>service factory creates</strong> a service, its parameters are the <strong>injection boundary</strong>, and the returned functions' <strong>closures retain</strong> the injected dependencies.</p>
<p>At the service level, calling <code>makeRegistrationService(users)</code> supplies the contract-typed dependency and creates the service. Every returned function then closes over <code>users</code>. This is the direct functional counterpart of a constructor receiving a value, assigning it to a private field, and producing an object instance, without exposing the dependency to every service call.</p>
<p>At the application level, a composition function can invoke several service factories and build the graph. The next example generalises the earlier registration-only case into a composition function for the full application graph:</p>
<pre><code class="language-typescript">interface ApplicationDependencies {
  users: UserRepository;
  mailer: Mailer;
  clock: Clock;
}

function createApplication(dependencies: ApplicationDependencies) {
  const registration = makeRegistrationService(dependencies.users);
  const profile = makeProfileService(dependencies.users);
  const reminders = makeReminderService(dependencies.mailer, dependencies.clock);

  return {
    register: registration.register,
    getProfile: profile.get,
    sendReminders: reminders.send,
  };
}</code></pre>
<p>Call <code>createApplication(...)</code> once at the composition root, and each returned service closes over only the slice of the graph it needs; collectively, those services expose the complete, already-wired application. Production passes database, mail, and system-clock implementations. Tests pass in-memory repositories, recording mailers, and fixed clocks. The consumers remain unchanged.</p>
<p>Closure-based composition can make a Service Container unnecessary for a simple, static graph, but it is not a full replacement for the abstraction. The composition function builds the graph and each returned closure retains its relevant part. Together they cover the container's core composition responsibility: construct the graph centrally, bind implementations to contracts, and expose ready-to-use services. A container may additionally provide autowiring, named bindings, scopes, lifecycle management, decorators, interception, or plugin discovery, and it remains available in functional code when those capabilities earn their cost. In TypeScript, autowiring still needs runtime anchors such as classes or explicit string/symbol tokens; erased structural interfaces cannot be resolved directly. Manual composition is sufficient when those capabilities are not needed.</p>
<p>The two routes can now be summarised without treating module syntax as part of the comparison:</p>
<ul>
<li><strong>OOP:</strong> segregated interface → constructor/factory → dependencies retained in object fields → manual or container-assisted composition → root application object</li>
<li><strong>Functional TypeScript:</strong> structural contract → service factory → dependencies retained in closures → manual or container-assisted composition → application facade</li>
</ul>
<p>The architecture is preserved because each responsibility still has an explicit home. What changes is the default mechanism: lexical scope replaces instance fields, service factories replace construction through <code>new</code>, structural conformance replaces nominal declarations, and explicit composition can replace container resolution when the graph does not need richer container capabilities.</p>
<p>Anton van Straaten's fictional Qc Na koan captures the first equivalence: <em>&quot;objects are a poor man's closures; closures are a poor man's objects.&quot;</em> Both bundle behaviour with retained state. Here that state includes the in-memory repository's <code>Map</code> and, in each service closure, precisely the dependencies that service needs.</p>
<h2>Structural typing: the conformance mechanism</h2>
<p>Service factories and closures answer <em>how one service is created and retains its injected dependencies</em>; the composition root answers <em>where the full graph is assembled</em>. Structural typing answers a simpler question: how does the compiler know a value satisfies the contract its consumer expects? It's what makes the functional mapping safe, not just short.</p>
<p>In a structurally typed system, a value satisfies a type by having the right <strong>shape</strong>: the required members, with compatible types. Names and declarations are irrelevant. <code>UserRepository</code> defines one such shape:</p>
<pre><code class="language-typescript">interface UserRepository {
  findByDocument(document: string): Promise&lt;User | null&gt;;
  create(data: NewUser): Promise&lt;User&gt;;
}</code></pre>
<p>Any value with those two methods, and compatible signatures, has that shape. So when the in-memory repository assigns its object literal to that type. Consider:</p>
<pre><code class="language-typescript">export const inMemoryUserRepository: UserRepository = { findByDocument, create };</code></pre>
<p>That single line <strong>is the conformance check.</strong> It's compile-time, exhaustive, and free: if the object is missing a method, has one with an incompatible signature, or the contract later grows, this line stops compiling. No <code>implements</code>, no registration, no adapter class. And it generalises: any object literal, class instance, or factory result that has the shape <em>is</em> a <code>UserRepository</code>. The contract is decoupled from every implementation's ancestry, which is why an in-memory object literal and a Postgres-backed implementation can be substituted at the composition root without changing a consumer.</p>
<p>You can watch the check evaporate the moment it's done its job. The in-memory repository didn't need <code>implements</code>. A Postgres-backed class can still use it for clarity, but it's optional, and it disappears at runtime. The TypeScript source:</p>
<pre><code class="language-typescript">class PostgresUserRepository implements UserRepository {
  async findByDocument(document: string) { /* ... */ }
  async create(data: NewUser) { /* ... */ }
}</code></pre>
<p>compiles to plain JavaScript with no trace of the contract:</p>
<pre><code class="language-javascript">class PostgresUserRepository {
  async findByDocument(document) { /* ... */ }
  async create(data) { /* ... */ }
}</code></pre>
<p>The <code>interface</code> and <code>implements</code> annotation disappear because they are type-system constructs. The class remains because a class is also a runtime JavaScript construct. Conformance was proven once, before the program ran, and then discarded. It's also why a TypeScript DI container can't autowire by interface the way C# or PHP can: at runtime there's no <code>UserRepository</code> to resolve against, only classes and string/symbol tokens. The same erasure that keeps contracts lightweight is the reason reflection-based containers cannot autowire by interface and must use runtime classes or explicit tokens.</p>
<p>This is also where the <strong>I</strong> of SOLID becomes cheaper to practise. A small contract requires little implementation ceremony (no base class, no <code>implements</code>, no registration), so keeping interfaces role-specific adds less friction. In a nominal OOP language, each new segregated interface adds a declaration that its implementations must adopt; structural typing removes that declaration tax. It does not guarantee Interface Segregation, but it makes the principle easier to apply consistently.</p>
<p>The contrast that makes this concrete is a nominal language, PHP, the reference OOP case here:</p>
<pre><code class="language-php">final class PostgresUserRepository implements UserRepository { /* ... */ }

function register(UserRepository $users): void { /* ... */ }</code></pre>
<p>Here conformance is <em>declared</em>. An object with exactly the right methods but no <code>implements UserRepository</code> is <strong>not</strong> a <code>UserRepository</code>: pass it to <code>register()</code> and PHP throws a <code>TypeError</code>. Same shape, wrong ancestry, rejected. This is one reason adapters and containers become more prominent in nominal ecosystems: conformance is explicitly declared, so wiring becomes a first-class activity with its own tooling.</p>
<p>One distinction worth stating now, because it drives the rest of the post: TypeScript is structural for interfaces, object types, and (mostly) classes, and it checks that conformance <strong>statically</strong>, before the program runs. &quot;Checked statically&quot; is easy to wave past, but it's the entire difference between structural typing and the thing it gets mistaken for. And the fastest place to feel that difference isn't in production at all. It's the moment you sit down to write a test.</p>
<p>And I am a Test-Driven Developer. Therefore...</p>
<h2>Testing: where duck typing walks back in</h2>
<p>The style survives contact with production easily enough. The seam is typed, the closure captures a contract-typed value. Where it gets interesting is the first time you write a test.</p>
<p>To test a service in isolation, you need a stand-in for its dependency, a repository that never touches a database. In JS/TS we've done this for years with an ad-hoc object: just the methods the test exercises, nothing more. And here the old instinct kicks in, reach for a loose double:</p>
<pre><code class="language-typescript">// duck-typed double: no contract in sight
const users = { findByDocument: async () =&gt; null } as any;</code></pre>
<p>This works. The test runs, the service calls <code>findByDocument</code>, the call resolves. That's <strong>duck typing</strong>: the object is accepted because, at the moment of the call, it happens to have the method. Nothing checked its shape ahead of time; the <code>as any</code> explicitly threw the check away.</p>
<p>And it raises the question this whole post circles: <strong>is that the same thing as structural typing?</strong> It feels identical, both say &quot;shape is enough, ancestry is irrelevant.&quot; But the difference is exactly the <em>when</em>. Type the same double against the contract instead:</p>
<pre><code class="language-typescript">// structurally-typed double: checked against the contract, now
const users: UserRepository = {
  findByDocument: async () =&gt; null,
  create: async (data) =&gt; ({ id: &#039;x&#039;, ...data, createdAt: new Date() }),
};</code></pre>
<p>Now the compiler enforces the whole shape <em>before the test runs</em>. Forget <code>create</code>, or let the real <code>UserRepository</code> grow a method, and this double stops compiling; the test tells you it drifted. The <code>as any</code> version compiles happily and only fails later, at runtime, if that path is even exercised.</p>
<p>Same philosophy, opposite failure mode. The loose double (<code>as any</code>) is widely used and genuinely convenient, and it's quietly the reason so many of us assume duck typing and structural typing are one thing. They aren't: typing the double <code>: UserRepository</code> is the small discipline that keeps a test double in the structural camp, and it's worth doing. But the recommendation isn't the point here. The point is that the moment you write a mock is the moment the two concepts visibly separate, which is exactly what the next section pulls apart.</p>
<h2>Untangling the five concepts</h2>
<p>Five ideas have been doing distinct work so far. Placing them side by side makes the boundaries explicit:</p>
<table>
<thead>
<tr>
<th>Concept</th>
<th>What it is</th>
<th>What it is not</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Dependency inversion / injection</strong></td>
<td>DIP: a principle about <em>direction</em>; depend on abstractions (the D in SOLID). DI: a <em>technique</em> serving it; bind concretions elsewhere</td>
<td>A framework, a container, or anything inherently class-shaped</td>
</tr>
<tr>
<td><strong>OOP constructs</strong></td>
<td>Classes, constructors, <code>implements</code>; <em>one mechanism</em> for DI and encapsulation</td>
<td>The definition of DI, or a requirement for Interface Segregation or Dependency Inversion</td>
</tr>
<tr>
<td><strong>Closures</strong></td>
<td>Functions capturing their environment; <em>another mechanism</em> for retained private state, injected dependencies, and explicit graph composition</td>
<td>A Service Container abstraction by definition, or a hack until &quot;real&quot; OOP arrives</td>
</tr>
<tr>
<td><strong>Structural typing</strong></td>
<td><strong>Static</strong> conformance decided by shape; checked before the program runs</td>
<td>A dynamic-language feature; permission to skip contracts</td>
</tr>
<tr>
<td><strong>Duck typing</strong></td>
<td><strong>Runtime</strong> conformance: &quot;if it quacks&quot;; discovered at the call site, or not at all</td>
<td>A synonym for structural typing</td>
</tr>
</tbody>
</table>
<p>Read down the &quot;what it is not&quot; column and the responsibilities separate cleanly. <strong>Dependency injection is not a framework</strong>: an explicit composition function can bind the graph as truly as a container. <strong>OOP constructs are not the definition of DI</strong>: they're one mechanism among several, the one that happens to dominate nominal ecosystems. <strong>Closures are not a hack or a container by definition</strong>: they are a language mechanism that can retain one dependency or the complete composed graph.</p>
<p>The two that cause the most trouble are the last ones: <strong>structural typing vs duck typing.</strong> They share a philosophy, behaviour over ancestry, shape over name, and in TypeScript they even look identical to write, which is why they get treated as one thing. They are not. The difference is <em>when the shape is checked</em>. Structural typing checks it at <strong>compile time</strong>, by a type checker, before the program runs; the failure is a red squiggle in your editor. Duck typing checks it at <strong>runtime</strong>, at the moment of the call, by the call itself; the failure is a thrown error, or worse, silence, if the missing method simply never gets reached on that path.</p>
<p>Which answers the question the testing section left open: if structural typing isn't what <em>makes duck typing possible</em>, what does? The enabler is <strong>dynamic dispatch</strong>, the language resolving <code>x.quack()</code> against whatever <code>x</code> actually is at call time, with no prior type commitment required. That's a property of the runtime, not of the type system, and it's independent of whether conformance is judged by shape or by declaration. The clean proof is Python: it has had duck typing forever, with no structural type system at all; <code>typing.Protocol</code> (PEP 544) later <em>added</em> static structural checking as an opt-in for external type checkers, and the duck-typed runtime underneath didn't change one bit. So duck typing and structural typing aren't two names for one idea, they're independent answers to two different questions, <em>how?</em> and <em>when?</em>, and a language can choose each answer separately.</p>
<h2>The language field guide</h2>
<p>Put the two questions on two axes, <strong>how</strong> conformance is decided (by shape, or by declaration) and <strong>when</strong> it's checked (statically at compile time, or at runtime), and every language lands in a cell:</p>
<table>
<thead>
<tr>
<th></th>
<th>Checked statically (compile time)</th>
<th>Checked at runtime</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>By shape</strong></td>
<td>TypeScript, Go (<em>structural typing</em>)</td>
<td>Python, JS, Ruby (<em>duck typing</em>)</td>
</tr>
<tr>
<td><strong>By declaration</strong></td>
<td>C#, Java (<em>nominal + static</em>)</td>
<td>PHP with type hints (<em>nominal + runtime</em>)</td>
</tr>
</tbody>
</table>
<p>The cells people forget are the interesting ones.</p>
<ul>
<li><strong>TypeScript</strong>: shape, static, and <em>loose</em>. An ad-hoc object literal conforms on the spot, no named type required. The cheapest contracts of any mainstream language. TypeScript provides static structural checking over a dynamically typed JavaScript runtime, so once the types are erased, what's left is duck typing.</li>
<li><strong>Go</strong>: shape, static, and <em>not loose</em>. Interfaces are satisfied implicitly (structural, a type never says <code>implements</code>), but conformance still needs methods declared on a named type; there are no ad-hoc conforming literals and no runtime &quot;try the call and see.&quot; Interface satisfaction operates through named interface types rather than TypeScript-style ad-hoc object compatibility. This is the answer to <strong>&quot;is there structural typing without any duck typing?&quot;</strong>. Go is it, and it isn't a compromise. The idiomatic Go move of defining tiny consumer-side interfaces is Interface Segregation and structural typing working together, straight out of the textbook.</li>
<li><strong>Python / JS / Ruby</strong>: shape, runtime. Classic duck typing, enabled by dynamic dispatch. Python then bolts on optional static structural typing via <code>typing.Protocol</code> (PEP 544) for anyone running a type checker, without touching the duck-typed runtime, the cleanest demonstration that the two are separable.</li>
<li><strong>PHP</strong>: declaration, runtime. Nominal, but enforced at call time. With type declarations, objects of classes that declare <code>implements</code> pass; a mismatch is a <code>TypeError</code> when the call happens, not a compile error (PHPStan/Psalm drag the check earlier, as a lint). Drop the declarations and PHP becomes dynamically typed: no type enforcement on parameters, method calls resolved at runtime, but objects are still class instances, not ad-hoc shapes. That double identity, nominal-when-typed, dynamic-when-not, plus autowiring by type declaration is the same story that made the containers from section 3 inevitable.</li>
<li><strong>C# / Java</strong>: declaration, static. Nominal, checked at compile time. The full ceremony (declare the interface, <code>implements</code> it, register it, inject it) and perfectly capable of clean dependency-inverted designs; the container exists to amortise the wiring cost the nominal type system creates in the first place.</li>
</ul>
<p>The grid shows these are <strong>independent axes</strong>, and real languages occupy all four cells. Go is structural and statically checked, but without TypeScript's ad-hoc object compatibility; TypeScript is structural-and-loose yet fully static; Python is duck-typed with an optional structural upgrade; PHP is nominal without being static, the quadrant people forget exists. &quot;Structural&quot; and &quot;duck&quot; are not synonyms, and &quot;nominal&quot; and &quot;static&quot; are not the same axis. Once the grid is visible, the testing question becomes precise: a TypeScript double can rely on JavaScript's runtime duck typing, or it can be checked structurally before the test runs. The object may look the same; the guarantee is different.</p>
<h2>A good way to use them</h2>
<p>None of this argues against classes, and none of it argues against containers. It argues for choosing the mechanism on purpose. A few defaults fall out of everything above:</p>
<ul>
<li><strong>Keep contracts in the domain, named in the domain's language.</strong> <code>UserRepository</code> belongs in <code>domain/user.ts</code>, not in the infrastructure that implements it; the consumer owns the interface, the implementation conforms to it. This is the Go consumer-defined-interface instinct and Interface Segregation in one move.</li>
<li>For simple functional services, a factory + closure is a natural default. The dependency is captured once and stays private. Keep <strong>parameter passing</strong> for genuinely local composition or deliberate scaffolding. Put the complete graph in an explicit <strong>composition root</strong> (<code>createApplication</code> or equivalent), invoke it once with production implementations, and retain the returned application facade as the wired root object. That is where implementation choices remain centralised and tests gain a single, typed substitution boundary.</li>
<li><strong>Treat class-vs-closure as a mechanism choice, not a moral one.</strong> Pick by team convention and by whether you genuinely need identity or lifecycle semantics, not because one of them is &quot;real&quot; dependency injection. They both are.</li>
</ul>
<p>It's worth naming the design principle underneath, because it's the reason the cheap option is also the good one. In Vlad Khononov's Balanced Coupling model, a shared contract is the <strong>weakest, most distance-tolerant</strong> form of coupling between two components, exactly what you want across a module boundary, where the two sides may change and ship independently. Structural typing makes that weakest level of coupling nearly free to express, so in TypeScript the best-balanced design and the least-ceremony design are the same design. The one discipline the model insists on: keep the wiring <strong>explicit and greppable</strong>, a named seam you can find, because Balanced Coupling's warning is against <em>implicit</em> coupling, and implicit coupling is also structural typing's failure mode. Which is the honest counterpoint.</p>
<h2>The honest counterpoint</h2>
<p>The price of making conformance free is that conformance becomes <em>implicit</em>. Anything with the right shape satisfies a contract, including things that match only by accident. A single-method contract like <code>{ execute(): void }</code> is satisfied by half the objects in a codebase; the type checker will happily accept a wrong one that merely fits. The narrower and more generic the contract, the more coincidental matches it invites, so the defence is to keep contracts <strong>role-named and non-trivial</strong> (<code>UserRepository</code>, not <code>Doer</code>), so that matching the shape actually means matching the intent.</p>
<p>Implicitness costs tooling, too. Without <code>implements</code>, &quot;find all implementations of this interface&quot; is a weaker query than in a nominal language, and renaming a contract member doesn't announce, at the declaration site, everyone who just broke. You lean harder on the compiler and on find-references, and a little less on the class hierarchy telling you who's involved.</p>
<p>The mitigation turns out to be the same thing as the recommendation from the previous section: <strong>an explicit, greppable composition root.</strong> A deliberate conformance point, <code>const impl: Contract = ...</code>, typed against the contract, living in that known location, turns implicit shape-matching back into a visible, findable decision. It's also, not coincidentally, the same discipline that keeps a <em>test double</em> structural rather than duck-typed. The functional style doesn't remove the need to be deliberate about coupling; it relocates the deliberateness from the type declaration to the wiring, and rewards you for keeping that wiring in the open.</p>
<h2>Checklist</h2>
<p>A quick test for &quot;am I doing DI properly in this style?&quot;, whether or not you reached for a class:</p>
<ul>
<li>Does the consumer depend on a <strong>contract</strong>, not a concrete implementation? (Take the contract as a factory parameter and capture it.)</li>
<li>Is the <strong>binding external</strong> to the consumer, captured by a service factory and selected at the composition root, rather than constructed inline?</li>
<li>Is every implementation, <strong>including test doubles</strong>, typed against the contract (<code>: UserRepository</code>), so drift fails at compile time instead of at runtime?</li>
<li>Are contracts <strong>small and role-named</strong>, so structural matching signals intent rather than coincidence?</li>
<li>Is the complete dependency graph <strong>explicit and greppable</strong> in one composition root, rather than having consumers select concrete implementations throughout the application?</li>
<li>Did you pick class-vs-closure for a <strong>real reason</strong> (convention, lifecycle, identity), not out of habit or a belief that one is &quot;real&quot; DI?</li>
</ul>
<hr />
<h2>Recap</h2>
<p>The article started with a refactor that removed a temporary parameter and raised a question: was I giving up Interface Segregation and Dependency Inversion by moving from OOP to functional TypeScript, or was I looking for OOP mechanisms where functional equivalents existed?</p>
<p>The answer turned out to be a comparison worth making. For every responsibility that had an obvious home in OOP, there was a functional technique that gave it an equivalent home:</p>
<ul>
<li><strong>Interface Segregation</strong> lives in small, role-named contracts. Structural typing removes the declaration tax that keeps contracts fat in nominal ecosystems.</li>
<li><strong>Dependency Inversion</strong> lives in depending on abstractions, not on concretions. A factory parameter captures a contract-typed dependency as cleanly as a constructor.</li>
<li><strong>Dependency Injection</strong> lives in binding concretions outside the consumer. A service factory and closure retain injected state as truly as an object field.</li>
<li><strong>Composition</strong> lives in a single root where implementations are selected and the graph is assembled. A composition function does this as truly as a Service Container.</li>
<li><strong>Conformance</strong> lives in shape, not ancestry. Structural typing checks it statically, before the program runs. Duck typing discovers it at runtime, at the call site. Same philosophy, different <em>when</em>.</li>
<li>The language field guide put that distinction on two independent axes:
<ul>
<li><strong>How</strong> conformance is decided: by shape or by declaration.</li>
<li><strong>When</strong> it's checked: statically at compile time, or at runtime.</li>
</ul>
</li>
<li>Real languages occupy all four cells:
<ul>
<li><strong>Go</strong> is structural and statically checked, but without ad-hoc object compatibility.</li>
<li><strong>TypeScript</strong> is structural and loose, yet fully static.</li>
<li><strong>Python</strong> is duck-typed with an optional structural upgrade.</li>
<li><strong>PHP</strong> is nominal without being static.</li>
</ul>
</li>
<li>&quot;Structural&quot; and &quot;duck&quot; are not synonyms, and &quot;nominal&quot; and &quot;static&quot; are not the same axis.</li>
<li>The honest counterpoint: making conformance free makes it implicit. Anything with the right shape satisfies a contract, including things that match only by accident. The defence is role-named, non-trivial contracts and an explicit, greppable composition root. A deliberate conformance point, typed against the contract, turns implicit shape-matching back into a visible, findable decision.</li>
<li>The principles stayed the same. The mechanisms changed:
<ul>
<li><strong>Classes, constructors, and</strong> <code>implements</code> are one set of mechanisms.</li>
<li><strong>Closures, factories, and structural contracts</strong> are another.</li>
</ul>
</li>
<li>Neither set is the definition of the principles they carry. Picking between them is a mechanism choice, not a moral one.</li>
<li>The one discipline that travels across both styles: <strong>keep the wiring explicit and greppable</strong>. A named seam you can find is the difference between deliberate coupling and accidental coupling, whether the seam is a constructor parameter or a factory argument.</li>
</ul>
<hr />
<h2>Glossary</h2>
<ul>
<li><strong>OOP — Object-Oriented Programming:</strong> a programming style that organises state and behaviour around objects. For comparison, this article uses a common class-based arrangement: interfaces, constructors, implementations, and a Service Container.</li>
<li><strong>SOLID:</strong> a family of five design principles associated with maintainable and adaptable software. This article focuses only on its <strong>I</strong> and <strong>D</strong>.</li>
<li><strong>ISP — Interface Segregation Principle:</strong> consumers should not be forced to depend on operations they do not use; prefer small, role-specific contracts.</li>
<li><strong>DIP — Dependency Inversion Principle:</strong> high-level policy should not depend directly on low-level details; both should depend on abstractions, and details should depend on those abstractions.</li>
<li><strong>DI — Dependency Injection:</strong> a technique for supplying dependencies from outside the consumer. DI can support DIP, but the two are not synonyms.</li>
<li><strong>Closure:</strong> a function together with the lexical environment it retains, allowing injected values to remain available after the factory that received them has returned.</li>
<li><strong>Service Container:</strong> a central assembler or registry that selects implementations and wires services, often with additional lifecycle or resolution features.</li>
<li><strong>Composition root:</strong> the single application location where concrete implementations are selected and the dependency graph is assembled.</li>
<li><strong>Seam:</strong> a place in code where behaviour can be varied without editing the code that uses it. Coined by Michael Feathers in <em>Working Effectively with Legacy Code</em>. In this article, the repository parameter is a seam: you swap implementations at the call site without touching the service.</li>
</ul>
<h2>Further reading and references</h2>
<h3>Glossary concepts</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Glossary/OOP">OOP - MDN glossary</a> — concise introduction to object-oriented programming and JavaScript's prototype-based model.</li>
<li><a href="https://en.wikipedia.org/wiki/SOLID">SOLID - Wikipedia</a> — the five-principle family; this article focuses on ISP and DIP.</li>
<li><a href="https://objectmentor.com/resources/articles/isp.pdf">The Interface Segregation Principle - Robert C. Martin</a> (PDF) — the original detailed treatment of cohesive, client-specific interfaces.</li>
<li><a href="https://objectmentor.com/resources/articles/dip.pdf">The Dependency Inversion Principle - Robert C. Martin</a> (PDF) — the original statement that policy and detail should depend on abstractions.</li>
<li><a href="https://www.martinfowler.com/articles/injection.html">Inversion of Control Containers and the Dependency Injection Pattern — Martin Fowler</a> — dependency injection, container wiring, and the separation of configuration from use.</li>
<li><a href="https://blog.ploeh.dk/2011/07/28/CompositionRoot/">Composition Root - Mark Seemann</a> — the application location where modules and concrete dependencies are assembled.</li>
<li><a href="https://www.informit.com/store/working-effectively-with-legacy-code-9780131177055">Working Effectively with Legacy Code - Michael Feathers</a> — the origin of the &quot;seam&quot; concept: a place in code where behaviour can be varied without editing the code that uses it.</li>
</ul>
<h3>Language and design mechanics</h3>
<ul>
<li><a href="https://www.typescriptlang.org/docs/handbook/type-compatibility">TypeScript Handbook - Type Compatibility</a> — TypeScript's structural type system.</li>
<li><a href="https://go.dev/ref/spec">The Go Programming Language Specification - Interface types</a> — implicit, statically checked interface satisfaction.</li>
<li><a href="https://peps.python.org/pep-0544/">PEP 544 - Protocols: Structural Subtyping</a> — Python's opt-in static structural typing.</li>
<li><a href="https://www.php.net/manual/en/language.types.type-system.php">PHP Manual - Type System</a> — PHP's nominal type system and runtime verification.</li>
<li><a href="https://coupling.dev/">Balanced Coupling - Vlad Khononov</a> — integration strength, distance, volatility, and contract coupling.</li>
<li><a href="https://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html">The Qc Na closures/objects koan - Anton van Straaten</a> — the equivalence between retained state in closures and objects.</li>
<li><a href="https://www.infoq.com/presentations/Simple-Made-Easy/">Simple Made Easy - Rich Hickey</a> — separating simplicity from familiarity and convenience.</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/the-principle-is-not-the-mechanism-isp-dip-and-language-structures-in-practice.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2420</post-id>	</item>
		<item>
		<title>Introduce Parameter Object &#124; Refactoring Patterns</title>
		<link>https://rafael.bernard-araujo.com/introduce-parameter-object-refactoring-patterns.php</link>
					<comments>https://rafael.bernard-araujo.com/introduce-parameter-object-refactoring-patterns.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Wed, 22 Apr 2026 05:45:10 +0000</pubDate>
				<category><![CDATA[Code example]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Refactoring Patterns]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[refactoring patterns]]></category>
		<category><![CDATA[rust]]></category>
		<category><![CDATA[software architecture]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2357</guid>

					<description><![CDATA[This refactoring pattern involves grouping parameters that naturally go together into a single object. When you see a group of data items that regularly travel together, appearing in function after function, it\'s a sign they should be combined into a single object. Benefits: - Reduces the number of parameters (improved readability) - Groups related data [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>This refactoring pattern involves grouping parameters that naturally go together into a single object. When you see a group of data items that regularly travel together, appearing in function after function, it\'s a sign they should be combined into a single object. Benefits: - Reduces the number of parameters (improved readability) - Groups related data together (better organisation) - Makes relationships between data explicit - Easier to add new related data without changing function signatures - Enables moving behaviour related to the data into the new class - Reduces errors from parameter ordering mistakes When to use: - When you have a group of parameters that often appear together - When you see the same parameters in multiple function signatures - When parameters represent a cohesive concept - When you need to add more related parameters - When parameter ordering becomes confusing See <a href="https://refactoring.com/catalog/introduceParameterObject.html">https://refactoring.com/catalog/introduceParameterObject.html</a> <strong>BEFORE: Multiple primitive parameters passed around</strong> Problems: - Too many parameters (hard to remember and maintain) - Parameters are related, but the relationship is not explicit - Easy to mix up parameter order - Adding new related data requires changing all function signatures - Difficult to add validation or behaviour for the group ```php declare(strict_types=1);</p>
<p>namespace RefactoringPatterns;</p>
<p>class IntroduceParameterObject<br />
{<br />
public function amountInvoicedBefore(<br />
\DateTimeImmutable $startDate,<br />
\DateTimeImmutable $endDate,<br />
string $customerName,<br />
string $customerId,<br />
string $customerEmail<br />
): float {<br />
$amount = 0;<br />
foreach ($this-getInvoices() as $invoice) { if ($this-&gt;isInDateRangeBefore($invoice, $startDate, $endDate) &amp;&amp; $this-&gt;isForCustomerBefore($invoice, $customerName, $customerId, $customerEmail)) { $amount += $invoice[\'amount\']; } } return $amount; } private function isInDateRangeBefore( array $invoice, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate ): bool { return $invoice[\'date\'] &gt;= $startDate &amp;&amp; $invoice[\'date\'] &lt;= $endDate; } private function isForCustomerBefore( array $invoice, string $customerName, string $customerId, string $customerEmail ): bool { return $invoice[\'customerId\'] === $customerId; } public function demonstratePattern(): void { $startDate = new \DateTimeImmutable(\'2024-01-01\'); $endDate = new \DateTimeImmutable(\'2024-03-31\'); $customerName = \'John Doe\'; $customerId = \'C001\'; $customerEmail = \'john@example.com\'; echo BEFORE (5 separate parameters):\n; $amountBefore = $this-&gt;amountInvoicedBefore( $startDate, $endDate, $customerName, $customerId, $customerEmail ); echo Amount invoiced: $ . number_format($amountBefore, 2) . \n; echo Issues: Too many parameters, unclear relationships\n\n; } } <code><code> **AFTER: Using parameter objects for related data** Benefits: - Reduced the chance of parameter ordering errors - Clear, self-documenting function signatures - Related data is explicitly grouped - Easy to add new related fields without changing signatures - Can add behaviour and validation to the parameter objects </code></code>php declare(strict_types=1);</p>
<p>namespace RefactoringPatterns;</p>
<p>class IntroduceParameterObject<br />
{<br />
public function amountInvoicedAfter(DateRange $dateRange, Customer $customer): float<br />
{<br />
$amount = 0;<br />
foreach ($this-getInvoices() as $invoice) { if ($this-&gt;isInDateRangeAfter($invoice, $dateRange) &amp;&amp; $this-&gt;isForCustomerAfter($invoice, $customer)) { $amount += $invoice[\'amount\']; } } return $amount; } private function isInDateRangeAfter(array $invoice, DateRange $dateRange): bool { return $dateRange-&gt;contains($invoice[\'date\']); } private function isForCustomerAfter(array $invoice, Customer $customer): bool { return $invoice[\'customerId\'] === $customer-&gt;id; } public function demonstratePattern(): void { echo AFTER (2 parameter objects):\n; $dateRange = new DateRange($startDate, $endDate); $customer = new Customer($customerId, $customerName, $customerEmail); $amountAfter = $this-&gt;amountInvoicedAfter($dateRange, $customer); echo Amount invoiced: $ . number_format($amountAfter, 2) . \n; echo Benefits: Clear grouping, can add validation and behavior\n\n; } } /<strong> <em> Parameter Object: DateRange </em> Encapsulates a range of dates with validation and behavior */ class DateRange { public function <strong>construct( private readonly \DateTimeImmutable $startDate, private readonly \DateTimeImmutable $endDate ) { if ($endDate &lt; $startDate) { throw new \InvalidArgumentException(\'End date must be after start date\'); } } public function getStartDate(): \DateTimeImmutable { return $this-&gt;startDate; } public function getEndDate(): \DateTimeImmutable { return $this-&gt;endDate; } /<strong> <em> Behavior: Check if a date is within the range </em>/ public function contains(\DateTimeImmutable $date): bool { return $date &gt;= $this-&gt;startDate &amp;&amp; $date &lt;= $this-&gt;endDate; } /</strong> <em> Behavior: Get the duration of the range in days </em>/ public function getDurationInDays(): int { return $this-&gt;startDate-&gt;diff($this-&gt;endDate)-&gt;days; } } /*<em> </em> Parameter Object: Customer <em> Encapsulates customer-related data with validation </em>/ class Customer { public readonly string $id; public readonly string $name; public readonly string $email; public function </strong>construct(string $id, string $name, string $email) { if (empty($id)) { throw new \InvalidArgumentException(\'Customer ID cannot be empty\'); } if (empty($name)) { throw new \InvalidArgumentException(\'Customer name cannot be empty\'); } if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { throw new \InvalidArgumentException(\'Invalid email address\'); } $this-&gt;id = $id; $this-&gt;name = $name; $this-&gt;email = $email; } /</strong> <em> Behavior: Get customer display name </em>/ public function getDisplayName(): string { return {$this-&gt;name} ({$this-&gt;email}); } } <code><code> Another example - Coordinates passed as primitives Before </code></code>php declare(strict_types=1);</p>
<p>namespace RefactoringPatterns;</p>
<p>class IntroduceParameterObject<br />
{<br />
public function calculateDistanceBefore(<br />
float $x1,<br />
float $y1,<br />
float $x2,<br />
float $y2<br />
): float {<br />
$dx = $x2 - $x1;<br />
$dy = $y2 - $y1;<br />
return sqrt($dx <em> $dx + $dy </em> $dy);<br />
}</p>
<pre><code>public function findNearbyLocationsBefore(
    float $centerX,
    float $centerY,
    float $radius,
    array $locations
): array {
    $nearby = [];
    foreach ($locations as $location) {
        $distance = $this-calculateDistanceBefore( $centerX, $centerY, $location[\'x\'], $location[\'y\'] ); if ($distance <= $radius) { $nearby[] = $location; } } return $nearby; } public function demonstratePattern(): void { echo Example 2: Location Distance Calculation\\n; echo -----------------------------------------\\n; $locations = [ [\'name\' => \'Store A\', \'x\' => 10.0, \'y\' => 20.0], [\'name\' => \'Store B\', \'x\' => 15.0, \'y\' => 25.0], [\'name\' => \'Store C\', \'x\' => 50.0, \'y\' => 50.0], ]; echo BEFORE (4 coordinate parameters):\\n; $nearbyBefore = $this->findNearbyLocationsBefore(10.0, 20.0, 10.0, $locations); echo Found  . count($nearbyBefore) .  nearby locations\\n; echo Issues: Easy to mix up x1, y1, x2, y2 parameters\\n\\n; } } ``<code> After </code>``php declare(strict_types=1);</code></pre>
<p>namespace RefactoringPatterns;</p>
<p>class IntroduceParameterObject<br />
{<br />
public function calculateDistanceAfter(Point $point1, Point $point2): float<br />
{<br />
return $point1-distanceTo($point2); } public function findNearbyLocationsAfter( Point $center, float $radius, array $locations ): array { $nearby = []; foreach ($locations as $locationData) { $location = new Point($locationData[\'x\'], $locationData[\'y\']); if ($center-&gt;distanceTo($location) &lt;= $radius) { $nearby[] = $locationData; } } return $nearby; } public function demonstratePattern(): void { // Example 2: Coordinate calculations echo Example 2: Location Distance Calculation\n; echo -----------------------------------------\n; $locations = [ [\'name\' =&gt; \'Store A\', \'x\' =&gt; 10.0, \'y\' =&gt; 20.0], [\'name\' =&gt; \'Store B\', \'x\' =&gt; 15.0, \'y\' =&gt; 25.0], [\'name\' =&gt; \'Store C\', \'x\' =&gt; 50.0, \'y\' =&gt; 50.0], ]; echo AFTER (Point parameter object):\n; $center = new Point(10.0, 20.0); $nearbyAfter = $this-&gt;findNearbyLocationsAfter($center, 10.0, $locations); echo Found  . count($nearbyAfter) .  nearby locations\n; echo Benefits: Point object can now have behavior (distanceTo method)\n\n; echo === Key Benefits ===\n; echo 1. Function signatures are clearer and more maintainable\n; echo 2. Related data is explicitly grouped together\n; echo 3. Behavior can be added to parameter objects\n; echo 4. Easier to extend without breaking existing code\n; echo 5. Reduced chance of parameter ordering mistakes\n; echo 6. Parameter objects can enforce validation rules\n; } } /*<em> </em> Parameter Object: Point <em> Encapsulates 2D coordinates with geometric operations </em>/ class Point { public function __construct( private readonly float $x, private readonly float $y ) { } public function getX(): float { return $this-&gt;x; } public function getY(): float { return $this-&gt;y; } /<strong> <em> Behavior: Calculate distance to another point </em>/ public function distanceTo(Point $other): float { $dx = $other-&gt;x - $this-&gt;x; $dy = $other-&gt;y - $this-&gt;y; return sqrt($dx <em> $dx + $dy </em> $dy); } /</strong> <em> Behavior: Create a new point offset from this one </em>/ public function offset(float $dx, float $dy): Point { return new Point($this-&gt;x + $dx, $this-&gt;y + $dy); } } ```</p>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/introduce-parameter-object-refactoring-patterns.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2357</post-id>	</item>
		<item>
		<title>Tropeçando 117</title>
		<link>https://rafael.bernard-araujo.com/tropecando-117.php</link>
					<comments>https://rafael.bernard-araujo.com/tropecando-117.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Thu, 20 Nov 2025 08:02:00 +0000</pubDate>
				<category><![CDATA[Tropeçando]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[llm]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[site reliability]]></category>
		<category><![CDATA[software engineering]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2221</guid>

					<description><![CDATA[How far can we push AI autonomy in code generation? We ran a series of experiments to explore how far Generative AI can currently be pushed toward autonomously developing high-quality, up-to-date software without human intervention. As a test case, we created an agentic workflow to build a simple Spring Boot application end to end. We [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://martinfowler.com/articles/pushing-ai-autonomy.html">How far can we push AI autonomy in code generation?</a></p>
<blockquote>
<p>We ran a series of experiments to explore how far Generative AI can currently be pushed toward autonomously developing high-quality, up-to-date software without human intervention. As a test case, we created an agentic workflow to build a simple Spring Boot application end to end. We found that the workflow could ultimately generate these simple applications, but still observed significant issues in the results—especially as we increased the complexity. The model would generate features we hadn't asked for, make shifting assumptions around gaps in the requirements, and declare success even when tests were failing. We concluded that while many of our strategies — such as reusable prompts or a reference application — are valuable for enhancing AI-assisted workflows, a human in the loop to supervise generation remains essential. </p>
</blockquote>
<p><a href="https://thephp.foundation/blog/2025/09/05/php-mcp-sdk/">Announcing the Official PHP SDK for MCP</a></p>
<blockquote>
<p>The PHP Foundation, Anthropic’s MCP team, and Symfony are collaborating on the official PHP SDK for the Model Context Protocol (MCP). Our goal is a framework-agnostic, production-ready reference implementation the PHP ecosystem can rely on.</p>
</blockquote>
<p><a href="https://ashallendesign.co.uk/blog/covariance-and-contravariance-in-php">Covariance and Contravariance in PHP </a></p>
<blockquote>
<p>Before we dive into the details and code examples, let me quickly define covariance and contravariance:</p>
<p>Covariance: Making something more specific<br />
Contravariance: Making something less specific</p>
<p>Now let's dive in and see how these concepts apply to PHP.</p>
</blockquote>
<p><a href="https://slack.engineering/break-stuff-on-purpose/">Break Stuff on Purpose</a></p>
<blockquote>
<p>Strengthen your system’s ability to recover by intentionally causing and resolving failures</p>
</blockquote>
<p><a href="https://read.thecoder.cafe/p/nothing-beats-kindness">Nothing Beats Kindness</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/tropecando-117.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2221</post-id>	</item>
		<item>
		<title>Tropeçando 116</title>
		<link>https://rafael.bernard-araujo.com/tropecando-116.php</link>
					<comments>https://rafael.bernard-araujo.com/tropecando-116.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Tue, 26 Aug 2025 08:30:54 +0000</pubDate>
				<category><![CDATA[Miscelaneous]]></category>
		<category><![CDATA[e2e test]]></category>
		<category><![CDATA[microservices]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[software testing]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2142</guid>

					<description><![CDATA[End-to-end testing across bounded contexts in a microservices environment requires a careful balance of responsibilities and collaboration. And depending on how your organisation is structured, different teams are responsible for testing parts or the entirety of the user journey. Check Yan Cui answering &#34;How to end-to-end test microservices across bounded contexts?&#34; Do you know you [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>End-to-end testing across bounded contexts in a microservices environment requires a careful balance of responsibilities and collaboration. And depending on how your organisation is structured, different teams are responsible for testing parts or the entirety of the user journey. Check Yan Cui answering <em>&quot;<a href="https://theburningmonk.com/2024/12/how-to-e2e-test-microservices-across-bounded-contexts/">How to end-to-end test microservices across bounded contexts?</a>&quot;</em></p>
<p>Do you know you can make use of PHP XDEBUG and <a href="https://laravel-news.com/flexible-docker-images-with-php-ini-environment-variables">make your Docker images flexible using environment variables with INI settings</a>? Paul Redmond shows you how to use Xdebug's built-in environment variables to configure things if you prefer simplicity in a team's environment, so you can still have the INI settings ready in a way you can enable or disable whenever it suits.</p>
<p><a href="https://theburningmonk.com/2025/04/how-to-use-neon-and-ephemeral-environments-to-simplify-serverless-development/">How to use Neon and ephemeral environments to simplify serverless development</a>, with Yan Cui.</p>
<p><a href="https://newsletter.goodtechthings.com/p/harsh-truths-to-save-you-from-chatgpt">Harsh truths to save you from ChatGPT psychosis</a></p>
<blockquote>
<p>Once you come to believe that you are sort of a minor cybernetic deity, you have lost touch with reality in a subtle, terrifying way. You are at the mercy of whatever weird fantasy the LLM spits out next.</p>
<p>The major LLMs are all in a sycophantic phase right now, which doesn’t help, but I doubt that “make the chatbots less encouraging” is an easy fix here. We are dealing with a technology uniquely suited to snipe intelligent, well-educated people into believing they are much, much smarter than they really are. That is an addictive sensation, not easily quit.</p>
</blockquote>
<p><a href="https://frederickvanbrabant.com/blog/2025-07-22-the-real-ask/">The real ask</a></p>
<blockquote>
<p>The question we receive is not always the problem we need to solve.</p>
</blockquote>
<p>Asking the right question to understand the real problem. And to provide the true solution.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/tropecando-116.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2142</post-id>	</item>
		<item>
		<title>Tropeçando 115</title>
		<link>https://rafael.bernard-araujo.com/tropecando-115.php</link>
					<comments>https://rafael.bernard-araujo.com/tropecando-115.php#comments</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Sun, 29 Dec 2024 07:10:02 +0000</pubDate>
				<category><![CDATA[Tropeçando]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[aws-cdk]]></category>
		<category><![CDATA[design pattern]]></category>
		<category><![CDATA[lazy loading]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[project management]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2098</guid>

					<description><![CDATA[PHP is the Best Choice for Long‑Term Business Mature and health ecosystem, long-term stability with clear release cycles, at least two well-proven frameworks, self-reflecting Technology and open-source adaptation to the future are points to put PHP as a great (if not the best) choice for long-term business. Optimize for optionality and build towards checkpoints Optimize [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://tomasvotruba.com/blog/php-is-the-best-choice-for-long-term-business">PHP is the Best Choice for Long‑Term Business</a></p>
<blockquote>
<p>Mature and health ecosystem, long-term stability with clear release cycles, at least two well-proven frameworks, self-reflecting Technology and open-source adaptation to the future are points to put PHP as a great (if not the best) choice for long-term business.</p>
</blockquote>
<p><a href="https://sebastiandedeyne.com/optimize-for-optionality-and-build-towards-checkpoints">Optimize for optionality and build towards checkpoints </a></p>
<blockquote>
<p>Optimize for optionality and build towards checkpoints</p>
<p>To make this plea actionable: treat each chunk of work as something that should be merged by the end of the week. That doesn't mean it needs to be &quot;done&quot; or available to the end user, it needs to become a citizen of The Codebase.</p>
</blockquote>
<p><a href="https://jolicode.com/blog/php-object-lazy-loading-is-more-than-what-you-think">PHP Object Lazy-Loading is More Than What You Think</a></p>
<blockquote>
<p>In short: lazy-loading consists of delaying load or initialization of resources or objects until they’re actually needed. It’s something you will never see directly, the whole objective of lazy-loading is to be invisible so you can use your applications the way you always do.</p>
<p>Check this blog posts, which talks about this pattern, a new PHP RFC and some ways you can boost the performance of your application when you use lots of API calls.</p>
</blockquote>
<p><a href="https://opensourcepledge.com/">Open Source Pledge</a></p>
<blockquote>
<p>What is the Open Source Pledge?</p>
<p>Open Source Pledge is a group of companies with a shared commitment to paying the maintainers of the Open Source software we all consume. Our goal is to establish a new social norm in the tech industry of companies paying Open Source maintainers, so that burnout and related security issues such as those in XZ and Apache Log4j can become a thing of the past.</p>
</blockquote>
<p><a href="https://blog.jetbrains.com/idea/2024/10/code-analysis-for-your-projects-with-intellij-idea-and-qodana/">Code Analysis for Your Projects With IntelliJ IDEA and Qodana</a></p>
<blockquote>
<p>As developers, we spend more time maintaining existing code than we do writing new code. Any tools that can help make this easier can save us a lot of time.</p>
</blockquote>
<p><a href="https://www.youtube.com/watch?v=Sr339AKcxT0">Live coding - Event Sourcing in PHP - Let's overcomplicate a single-page - Brendt Roose</a></p>
<blockquote>
<p>Brendt did a live coding session demonstrating how event-sourcing in PHP looks like. He used <a href="https://tempestphp.com/">Tempest framework</a>, but concepts apply to any application.</p>
</blockquote>
<p><a href="https://blog.serverlessadvocate.com/configuring-aws-cdk-apps-across-multiple-environments-f9e0f1158a70">Configuring AWS CDK Apps Across Multiple Environments</a></p>
<blockquote>
<p>In this quick article, we will cover how we can setup our deterministic serverless application configuration in a way that it can differ between different environments, and where the correct environment is deployed to the correct AWS accounts. We will do a super simple example to show the concepts.</p>
</blockquote>
<p><a href="https://medium.com/@volvogroup/how-to-successfully-adopt-serverless-in-large-organizations-2e0db1b72881">How to successfully adopt serverless in large organizations</a></p>
<blockquote>
<p>Why serverless?</p>
<p>Serverless has disrupted the tech industry in recent years and leveled the playing field by giving organizations instant access to the same resources. In fact, it gives startups and small organizations an edge. They can move faster because they have fewer processes in place than larger traditional organizations, and serverless enables this agility.</p>
</blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/tropecando-115.php/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2098</post-id>	</item>
		<item>
		<title>Tropeçando 114</title>
		<link>https://rafael.bernard-araujo.com/tropecando-114.php</link>
					<comments>https://rafael.bernard-araujo.com/tropecando-114.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Fri, 27 Sep 2024 04:44:36 +0000</pubDate>
				<category><![CDATA[Tropeçando]]></category>
		<category><![CDATA[aws-cdk]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[developer experience]]></category>
		<category><![CDATA[front-end]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[PostGreSQL]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[software architecture]]></category>
		<category><![CDATA[testing]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2076</guid>

					<description><![CDATA[What's new in PHP 8.4 Don't miss these great features Property hooks new without parentheses JIT changes RFC Implicit nullable types New HTML5 support array_find Serverless Ephemeral Environments with Serverful AWS Services How to successfully use ephemeral environments with serverful resources, with example in the AWS CDK and Typescript. Comparison of Serverless Development and Hosting [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://stitcher.io/blog/new-in-php-84">What's new in PHP 8.4</a></p>
<blockquote>
<p>Don't miss these great features</p>
<ul>
<li>Property hooks</li>
<li>new without parentheses</li>
<li>JIT changes RFC</li>
<li>Implicit nullable types</li>
<li>New HTML5 support</li>
<li>array_find</li>
</ul>
</blockquote>
<p><a href="https://blog.serverlessadvocate.com/serverless-ephemeral-environments-with-serverful-aws-services-c803d24b353f">Serverless Ephemeral Environments with Serverful AWS Services</a></p>
<blockquote>
<p>How to successfully use ephemeral environments with serverful resources, with example in the AWS CDK and Typescript.</p>
</blockquote>
<p><a href="https://dev.to/aws-builders/comparison-of-serverless-development-and-hosting-platforms-5dld">Comparison of Serverless Development and Hosting Platforms</a></p>
<blockquote>
<p>When designing solutions in the cloud, there is (almost) always more than one alternative for achieving the same goal.</p>
<p>One of the characteristics of cloud-native applications is the ability to have an automated development process (such as the use of CI/CD pipelines).</p>
<p>In this blog post, I will compare serverless solutions for developing and hosting web and mobile applications in the cloud. </p>
</blockquote>
<p><a href="https://vnegrisolo.github.io/postgresql/generate-fake-data-using-sql">Generating fake data using SQL</a></p>
<blockquote>
<p>Fake data are very useful in development environment for testing your application or some query performances for example.</p>
</blockquote>
<p><a href="https://khalilstemmler.com/articles/client-side-architecture/introduction/">Client-Side Architecture Basics [Guide]</a></p>
<blockquote>
<p>Though the tools we use to build client-side web apps have changed substantially over the years, the fundamental principles behind designing robust software have remained relatively the same. In this guide, we go back to basics and discuss a better way to think about the front-end architecture using modern tools like React, xState, and Apollo Client.</p>
</blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/tropecando-114.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2076</post-id>	</item>
		<item>
		<title>Tropeçando 113</title>
		<link>https://rafael.bernard-araujo.com/tropecando-113.php</link>
					<comments>https://rafael.bernard-araujo.com/tropecando-113.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Fri, 16 Aug 2024 05:13:13 +0000</pubDate>
				<category><![CDATA[Tropeçando]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[aws-cdk]]></category>
		<category><![CDATA[clean architecture]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[ddd]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[serverless]]></category>
		<category><![CDATA[software engineering]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[sqli]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=1976</guid>

					<description><![CDATA[Neon Serverless PostgreSQL database with real zero-scaling. The fully managed serverless Postgres with a generous free tier. We separate storage and compute to offer autoscaling, branching, and bottomless storage. Compute scales dynamically to ensure you're ready for peak hours. Compute scales to zero and cold storage offloads to S3 for cost efficiency. Create a fully [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://neon.tech">Neon</a></p>
<blockquote>
<p>Serverless PostgreSQL database with real zero-scaling. The fully managed serverless Postgres with a generous free tier. We separate storage and compute to offer autoscaling, branching, and bottomless storage.</p>
<p>Compute scales dynamically to ensure you're ready for peak hours. Compute scales to zero and cold storage offloads to S3 for cost efficiency. Create a fully managed serverless Postgres instance in seconds.</p>
</blockquote>
<p><a href="https://laravel-news.com/make-your-app-faster-with-php-83">Make your app faster with PHP 8.3</a></p>
<blockquote>
<p>PHP 8.3 is the latest version of PHP. It has exciting new features and major improvements in performance. By upgrading to 8.3, you can achieve a significant increase in speed. In this article, we dive into how PHP 8.3 can be a game changer. It can speed up your application's performance.</p>
</blockquote>
<p><a href="https://dzone.com/articles/owasp-top-10-explained-3-sql-injection?">OWASP Top 10 Explained: SQL Injection</a></p>
<blockquote>
<p>SQL Injection (SQLi) is a code injection technique that exploits a security vulnerability occurring in the database layer of an application.</p>
<p>The vulnerability is present when user inputs are either improperly filtered for string literal escape characters embedded in SQL statements or user input is not strongly typed and thereby unexpectedly executed.</p>
<p>This allows an attacker to manipulate SQL queries, enabling them to unauthorized access, modify, and delete data in the database. This can lead to significant breaches of confidentiality, integrity, and availability, ranging from unauthorized viewing of data to complete database compromise.</p>
</blockquote>
<p><a href="https://blog.serverlessadvocate.com/15-quick-useful-tips-for-aws-cdk-engineers-a7675e1557aa">15 Quick Useful Tips for AWS CDK Engineers</a></p>
<blockquote>
<p>In this short article, we will cover 15 useful tips with accompanying code snippets for AWS CDK users.</p>
</blockquote>
<p><a href="https://khalilstemmler.com/articles/typescript-domain-driven-design/repository-dto-mapper/">Implementing DTOs, Mappers &amp; the Repository Pattern using the Sequelize ORM [with Examples] - DDD w/ TypeScript</a></p>
<blockquote>
<p>There are several patterns that we can utilize in order to handle data access concerns in Domain-Driven Design. In this article, we talk about the role of DTOs, repositories &amp; data mappers in DDD.</p>
</blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/tropecando-113.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1976</post-id>	</item>
		<item>
		<title>A bref AWS PHP story – Part 3</title>
		<link>https://rafael.bernard-araujo.com/a-bref-aws-php-story-part-3.php</link>
					<comments>https://rafael.bernard-araujo.com/a-bref-aws-php-story-part-3.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Tue, 27 Feb 2024 07:44:50 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[aws-cdk]]></category>
		<category><![CDATA[bref]]></category>
		<category><![CDATA[bref-php-aws-story]]></category>
		<category><![CDATA[cdk]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[serverless]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=1980</guid>

					<description><![CDATA[We are starting Part 3 of the Series &#34;A bref AWS PHP history&#34;. You can check Part 1, where I presented the PHP language as a reliable and good alternative for Serverless applications and Part 2 where we see the usage of CDK features in favour of a faithful CI/CD. Part 3 is to show [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>We are starting Part 3 of the Series <a href="https://rafael.bernard-araujo.com/tag/bref-php-aws-story">&quot;A bref AWS PHP history&quot;</a>. You can check <a href="https://dev.to/rafaelbernard/a-bref-aws-php-history-part-1-2agn">Part 1</a>, where I presented the PHP language as a reliable and good alternative for Serverless applications and <a href="https://dev.to/rafaelbernard/a-bref-aws-php-story-part-2-1dhe">Part 2</a> where we see the usage of CDK features in favour of a faithful CI/CD.</p>
<p>Part 3 is to show the upgrade path to Bref 2 and to achieve more coverage of the AWS resources. We will use DynamoDB, a powerful database for serverless architectures.</p>
<p>Some of those topics seem straightforward to some people, but I would like to avoid guessing that this is known to the audience since I have experienced some PHP developers struggling to put all these together for the first time due to the paradigm change. It should be fun.</p>
<p>Table of contents:</p>
<ol>
<li>What else are we doing?</li>
<li>Describing more AWS services - Adding a DynamoDB table</li>
<li>Bref upgrade</li>
<li>Testing CDK</li>
<li>PHP and AWS Services</li>
<li>Wrap-up</li>
</ol>
<h2>What else are we doing?</h2>
<p>In this section, we'll explore additional functionalities and enhancements to our serverless application. Building upon the foundation laid in Part 2, we'll introduce new features and integrations to further extend the capabilities of our AWS PHP application.</p>
<p>The <a href="https://dev.to/rafaelbernard/a-bref-aws-php-story-part-2-1dhe">Part 2</a> uses the result of the Fibonacci of a provided integer or a random integer from 400 to 1000 (to get a good image and not to overflow <code>integer</code>). This integer is the number of pixels of an image from the bucket and an arbitrary request metadata we are creating. If the image does not exist, the lambda will fetch a random image from the web with that number of pixels, save it and generate the metadata.</p>
<p>The computing complexity is irrelevant because it could be very complex logic or very simple, and the topics we are discussing in this part of the series will use the same design.</p>
<p>The lambda will now search the metadata in a DynamoDB table, saving the metadata when it does not exist. DynamoDB is largely used in Lambda code.</p>
<p><a href="https://github.com/rafaelbernard/bref-initial-php-aws-story/tree/part-3">Get the part-3 source-code on GitHub</a> and <a href="https://github.com/rafaelbernard/bref-initial-php-aws-story/compare/tag-part-2...tag-part-3">the diff from part-2</a>.</p>
<h2>Describing more AWS services - Adding a DynamoDB Table</h2>
<p>DynamoDB plays a crucial role in serverless architectures, offering scalable and high-performance NoSQL database capabilities. In this section, we'll delve into the process of integrating DynamoDB into our AWS CDK stack, expanding our application's data storage and retrieval capabilities.</p>
<p><a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html">DynamoDB</a> is a fully managed NoSQL database service provided by AWS, offering seamless integration with other AWS services, automatic scaling, and built-in security features. Its scalability, low latency, and flexible data model make it well-suited for serverless architectures and applications with varying throughput requirements.</p>
<pre><code class="language-ts">    const table = new Table(this, TableName, {
      partitionKey: { name: &#039;PK&#039;, type: AttributeType.STRING },
      sortKey: { name: &#039;SK&#039;, type: AttributeType.STRING },
      removalPolicy: RemovalPolicy.DESTROY,
      tableName: TableName,
    });</code></pre>
<p>Following the same principles for creating other AWS resources, we utilize the AWS CDK to define a DynamoDB table within our stack. Let's dive into the key parameters of the Table constructor:</p>
<ul>
<li><code>partitionKey</code>: This parameter defines the primary key attribute for the DynamoDB table, used to distribute items across partitions for scalability. In our example, <code>{ name: &#039;PK&#039;, type: AttributeType.STRING }</code> specifies a partition key named 'PK' with a string type. The naming convention ('PK') is arbitrary and can be tailored to suit your application's needs.</li>
<li><code>sortKey</code>: For tables requiring a composite primary key (partition key and sort key), the sortKey parameter comes into play. Here, <code>{ name: &#039;SK&#039;, type: AttributeType.STRING }</code> defines a sort key named 'SK' with a string type. Like the partition key, the name and type of the sort key can be customized based on your data model.</li>
<li><code>removalPolicy</code>: This parameter determines the behaviour of the DynamoDB table when the CloudFormation stack is deleted. By setting <code>RemovalPolicy.DESTROY</code>, we specify that the table should be deleted (destroyed) along with the stack. Alternatively, you can opt for <code>RemovalPolicy.RETAIN</code> to preserve the table post-stack deletion, which may be useful for retaining data.</li>
</ul>
<p>By decoupling configuration from implementation, we adhere to SOLID principles, ensuring cleaner and more robust code. This approach fosters flexibility, allowing our code to seamlessly adapt to changes, such as modifications to the table name while maintaining its functionality.</p>
<p>The implementation code is aware that the name will come from an environment variable and will work with that (yes, if you think that test will be easy to write, you are right):</p>
<pre><code class="language-ts">    const lambdaEnvironment = {
      TableName,
      TableArn: table.tableArn,
      BucketName: brefBucket.bucketName,
    };</code></pre>
<h2>Bref Upgrade</h2>
<p><a href="https://bref.sh">Bref</a>, the PHP runtime for AWS Lambda, continually evolves to provide developers with the latest features and optimizations. In this section, we'll discuss the upgrade to Bref 2.0 and explore how it enhances the deployment process and performance of our serverless PHP applications.</p>
<p>In this section, we're upgrading our usage of <a href="https://bref.sh">Bref</a>, a PHP runtime for AWS Lambda, to version 2.0. Bref simplifies the deployment of PHP applications to AWS Lambda, enabling us to run PHP code serverlessly.</p>
<p>The upgrade involves modifying our AWS CDK code to utilize the new features and improvements introduced in Bref 2.0. One notable improvement is the automatic selection of the latest layer of the PHP version, which simplifies the deployment process and ensures that our Lambda functions run on the most up-to-date PHP environment available.</p>
<pre><code class="language-ts">  const getLambda = new PhpFunction(this, <code>${stackPrefix}${functionName}</code>, {
    handler: 'get.php',
    phpVersion: '8.3',
    runtime: Runtime.PROVIDED_AL2,
    code: packagePhpCode(join(__dirname, <code>../assets/get</code>), {
      exclude: ['test', 'tests'],
    }),
    functionName,
    environment: lambdaEnvironment,
  });</code></pre>
<ul>
<li><strong>`PhpFunction` Constructor</strong>: We&#039;re using the `PhpFunction` constructor provided by Bref to define our Lambda function. This constructor allows us to specify parameters such as the handler file, PHP version, runtime, code location, function name, and environment variables.</li>
<li>`handler`: Specifies the entry point file for our Lambda function, where the execution starts.</li>
<li>`phpVersion`: Defines the PHP version to be used by the Lambda function. In this case, we&#039;re using PHP version 8.3.</li>
<li>`runtime`: Indicates the Lambda runtime environment. Here, `Runtime.PROVIDED_AL2` signifies the use of the Amazon Linux 2 operating system.</li>
<li>`code`: Specifies the location of the PHP code to be deployed to Lambda.</li>
<li>`functionName`: Sets the name of the Lambda function.</li>
<li>`environment`: Allows us to define environment variables required by the Lambda function, such as database connection strings or configuration settings.</li>
</ul>
<p>By upgrading to Bref 2.0 and configuring our Lambda function accordingly, we ensure compatibility with the latest enhancements and optimizations provided by Bref, thereby improving the performance and reliability of our serverless PHP applications on AWS Lambda.</p>
<h2>Testing CDK</h2>
<p>Ensuring the correctness and reliability of our AWS CDK infrastructure is crucial for maintaining a robust serverless architecture. In this section, we&#039;ll delve into testing our CDK resources, focusing on the DynamoDB table we added in the previous section.</p>
<p>As described earlier, we utilized the AWS CDK to provision a DynamoDB table within our serverless stack. Now, let&#039;s ensure that the table is configured correctly and behaves as expected by writing tests using the CDK&#039;s testing framework.</p>
<p>First, let&#039;s revisit how we added the DynamoDB table:</p>
<pre><code class="language-ts">const table = new Table(this, TableName, {
  partitionKey: { name: 'PK', type: AttributeType.STRING },
  sortKey: { name: 'SK', type: AttributeType.STRING },
  removalPolicy: RemovalPolicy.DESTROY,
  tableName: TableName,
});</code></pre>
<p>In this code snippet, we define a DynamoDB table with specified attributes such as partition key, sort key, removal policy, and table name. Now, to ensure that this table is created with the correct configuration, we&#039;ll write tests using CDK&#039;s testing constructs.</p>
<p>Check the following thest:</p>
<pre><code class="language-ts">test('Should have DynamoDB', () => {
  expectCDK(stack).to(
    haveResource(
      'AWS::DynamoDB::Table',
      {
        "DeletionPolicy": "Delete",
        "Properties": {
          "AttributeDefinitions": [
            {
              "AttributeName": "PK",
              "AttributeType": "S",
            },
            {
              "AttributeName": "SK",
              "AttributeType": "S",
            },
          ],
          "KeySchema": [
            {
              "AttributeName": "PK",
              "KeyType": "HASH",
            },
            {
              "AttributeName": "SK",
              "KeyType": "RANGE",
            },
          ],
          "ProvisionedThroughput": {
            "ReadCapacityUnits": 5,
            "WriteCapacityUnits": 5,
          },
          "TableName": "BrefStory-table",
        },
        "Type": "AWS::DynamoDB::Table",
        "UpdateReplacePolicy": "Delete",
      },
      ResourcePart.CompleteDefinition,
    )
  );
});</code></pre>
<p>This test ensures that the DynamoDB table is created with the correct attribute definitions, key schema, provisioned throughput, table name, and other properties specified during its creation. By writing such tests, we validate that our CDK infrastructure is provisioned accurately and functions as intended.</p>
<h2>PHP and AWS Services</h2>
<p>Leveraging PHP in a serverless environment opens up new possibilities for interacting with AWS services. In this section, we&#039;ll examine how PHP code seamlessly integrates with various AWS services, following best practices for maintaining clean and modular code architecture.</p>
<p>This is the part where we have fewer serverless needs impacting the code, as the PHP code will follow the same logic we might be using to communicate with AWS services on any other platform overall (there are always some specific use cases).</p>
<p>The reuse of the same existing logic is excellent. It leverages the decision to keep using PHP when moving that workload to Serverless, as the bulk of the knowledge and already proven code would remain as-is. We may escape the trap of classifying that PHP code as legacy as if it should be avoided, terminated or halted.</p>
<p>As a side note, a few external layers of our software architecture are touched if a good software architecture was applied before. Therefore, during the implementation of this architectural change, it should be quick to realise how beneficial and time-saving it is to have a well-architectured application with a balanced decision for patterns, principles, and designs to be applied, ultimately giving flexibility to the application and its features.</p>
<p>The handler is simplified now and should accommodate everything to a class in the direction of following SRP, a principle that we are bringing to the code during the code bites:</p>
<h3>Applications, domains, infrastructure, etc</h3>
<p>Our `PicsumPhotoService` is still orchestrating the business logic. The Single Responsibility Principle and Inversion of Control are applied. We are injecting the specialized services in the constructor:</p>
<pre><code class="language-php">// readonly class PicsumPhotoService
    public function __construct(
        private HttpClientInterface $httpClient,
        private ImageStorageService $storageService,
        private ImageRepository $repository,
    )
    {
    }</code></pre>
<p>Each specialized service has all its dependencies injected in the constructor as well. We can see the factory instantiation:</p>
<pre><code class="language-php">    public static function createPicsumPhotoService(): PicsumPhotoService
    {
        return new PicsumPhotoService(
            HttpClient::create(),
            new S3ImageService(
                new S3Client(),
                getenv('BucketName'),
            ),
            new DynamoDbImageRepository(
                new DynamoDbClient(),
                getenv('TableName'),
            ),
        );
    }</code></pre>
<p>The `ImageStorageService` will handle all image operations, connecting to the AWS Service when appropriate and observing business logic details. This is a slim interface:</p>
<pre><code class="language-php">interface ImageStorageService
{
    public function getImageFromBucket(int $imagePixels): ?array;

    public function saveImage(int $imagePixels, mixed $fetchedImage): void;

    public function createAndPutMetadata(int $imagePixels, array $metadata): PutObjectOutput;
}</code></pre>
<p>Instead of `: PutObjectOutput`, usually we would return a domain object, to not couple the interface with implementation details of using S3 Services, but for simplicity, I did not create a domain object here. It would be preferable though.</p>
<p>The `ImageRepository` will handle all metadata operations. It will save into a repository and observe logic details as well. Following the same principles, this is a slim interface:</p>
<pre><code class="language-php">interface ImageRepository
{
    public function findImage(int $imagePixels): ImageMetadataItem;

    public function addImageMetadata(ImageMetadataItem $imageMetadataItem): PutItemOutput;
}</code></pre>
<p>The `ImageMetadataItem` is a representation of one of the domain objects we have in our codebase.</p>
<pre><code class="language-php">readonly class ImageMetadataItem
{
    public function __construct(public int $imagePixels, public array $metadata)
    {
    }

    public function toDynamoDbItem(): array
    {
        return [
            'PK' => new AttributeValue(['S' => 'IMAGE']),
            'SK' => new AttributeValue(['S' => "PIXELS#{$this->imagePixels}"]),
            'pixels' => new AttributeValue(['N' => "{$this->imagePixels}"]),
            'metadata' => new AttributeValue(['S' => json_encode($this->metadata)]),
            ...ConvertToDynamoDb::item($this->metadata),
        ];
    }

    /**
     * @param array<string, AttributeValue> $item
     */
    public static function fromDynamoDb(array $item): static
    {
        return new static(
            (int) $item['pixels']->getN(),
            (array) json_decode($item['metadata']->getS()),
        );
    }
}</code></pre>
<p>If you check the implementation details, it operates transparently with all the services, business logic and AWS Services without any high couple with them. There are two utility functions:</p>
<ul>
<li><code>toDynamoDbItem</code>: to transform the object into a valid DynamoDb Item to be added</li>
<li><code>fromDynamoDb</code>: to perform the opposite operation, transforming a DynamoDb Item into a domain object</li>
</ul>
<p>The scope of the operation is very clear and does not bring the domain into dependency on those services, as the domain object can be used independently. It does not block any other way of dealing with it, giving the usage with other types of services, such as different databases or APIs. This is very important to the maintainability of the application without sacrificing the ease of readiness as it keeps the context of the utilities in the right place.</p>
<p>If you check all PHP code carefully, Bref is such a great abstraction layer that, removing the code from the handler file, any other line of code can be used as a lambda or a web application interchangeably without changing any line of code. This is very powerful, as you can imagine how you can leverage and migrate some of the existing code to lambda by just creating a handler that will trigger your existing code, if the code is well structured.</p>
<h2>Wrap-up</h2>
<p>It would be simple like that. Check more details in the source code, install it and try it yourself. This project is ready to:</p>
<ul>
<li>Extend lambda function using Bref</li>
<li>Upgrade to use Bref 2.0</li>
<li>Create a DynamoDB table</li>
<li>Test the stack Cloudformation code</li>
<li>Separate the PHP logic</li>
<li>Have PHP communicating with AWS Services</li>
</ul>
<p>Links:</p>
<ul>
<li><a href="https://rafael.bernard-araujo.com/tag/bref-php-aws-story">https://rafael.bernard-araujo.com/tag/bref-php-aws-story</a></li>
<li><a href="https://bref.sh">https://bref.sh</a></li>
<li><a href="https://dev.to/rafaelbernard/a-bref-aws-php-history-part-1-2agn">https://dev.to/rafaelbernard/a-bref-aws-php-history-part-1-2agn</a></li>
<li><a href="https://dev.to/rafaelbernard/a-bref-aws-php-story-part-2-1dhe">https://dev.to/rafaelbernard/a-bref-aws-php-story-part-2-1dhe</a></li>
<li><a href="https://github.com/rafaelbernard/bref-initial-php-aws-story/tree/part-3">https://github.com/rafaelbernard/bref-initial-php-aws-story/tree/part-3</a></li>
<li><a href="https://github.com/rafaelbernard/bref-initial-php-aws-story/compare/tag-part-2...tag-part-3">https://github.com/rafaelbernard/bref-initial-php-aws-story/compare/tag-part-2...tag-part-3</a></li>
<li><a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html">https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html</a></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/a-bref-aws-php-story-part-3.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1980</post-id>	</item>
		<item>
		<title>Tropeçando 112</title>
		<link>https://rafael.bernard-araujo.com/tropecando-112.php</link>
					<comments>https://rafael.bernard-araujo.com/tropecando-112.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Mon, 22 Jan 2024 01:09:51 +0000</pubDate>
				<category><![CDATA[Tropeçando]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[blue-green]]></category>
		<category><![CDATA[codedeploy]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[serverless]]></category>
		<category><![CDATA[terraform]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=1862</guid>

					<description><![CDATA[Treezor: a serverless banking platform This case study dives into how Treezor went serverless for their banking platform. From legacy code running on servers to a serverless monolith, and then event-driven microservices on AWS with Bref. Treezor is a high available banking application running mostly in PHP. Wait, is cloud bad? Forrest Brazeal review 37signals [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://bref.sh/docs/case-studies/treezor">Treezor: a serverless banking platform</a></p>
<blockquote>
<p>This case study dives into how Treezor went serverless for their banking platform. From legacy code running on servers to a serverless monolith, and then event-driven microservices on AWS with Bref.</p>
<p>Treezor is a high available banking application running mostly in PHP.</p>
</blockquote>
<p><a href="https://newsletter.goodtechthings.com/p/wait-is-cloud-bad">Wait, is cloud bad?</a></p>
<blockquote>
<p>Forrest Brazeal review 37signals (Basecamp) movement from the Cloud back to DataCenter, their use-case and some reasoning about the mentioned arguments for Data Center.</p>
</blockquote>
<p><a href="https://dev.to/aws-builders/ecs-bluegreen-deployment-with-codedeploy-and-terraform-3gf1">ECS Blue/Green deployment with CodeDeploy and Terraform </a></p>
<p><a href="https://getrector.com/blog/how-to-make-rector-contribute-your-pull-requests-every-day">How to make Rector Contribute Your Pull Requests Every Day</a></p>
<blockquote>
<p>Do you enjoy making code-reviews with hundreds of rules in your head and adding extra work to the pull-request author?</p>
<p>We don't, so we let Rector for us in active code review.</p>
</blockquote>
<p><a href="https://tim.mcnamara.nz/post/643989589027078144/docker-for-the-late-majority">Docker for the late majority</a></p>
<blockquote>
<p>This is a guide for people who would like a brief introduction to Docker and are too afraid to ask for one. I get it. Everyone around you already seems to know what they’re talking about. Looking ignorant is no fun.</p>
</blockquote>
<p><a href="https://chrisshennan.com/blog/10-essential-phpini-tweaks-for-improved-web-performance">10 Essential PHP.ini Tweaks for Improved Web Performance</a></p>
<blockquote>
<p>If you're running a website or web application with PHP, you may have encountered issues with slow loading times, high memory usage, or other performance problems. Fortunately, there are several tweaks you can make to your PHP configuration file (php.ini) to optimize your scripts and improve your website's performance. In this article, I'll cover the top 10 most common changes you might need to make to your php.ini file for best performance.</p>
</blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/tropecando-112.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1862</post-id>	</item>
		<item>
		<title>Solving problems 1: ECS, Event Bridge Scheduler, PHP, migrations</title>
		<link>https://rafael.bernard-araujo.com/solving-problems-1-ecs-event-bridge-scheduler-php-migrations.php</link>
					<comments>https://rafael.bernard-araujo.com/solving-problems-1-ecs-event-bridge-scheduler-php-migrations.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Thu, 30 Nov 2023 08:40:27 +0000</pubDate>
				<category><![CDATA[Solving Problems]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[ecs fargate]]></category>
		<category><![CDATA[EventBridge]]></category>
		<category><![CDATA[migration]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[test automation]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=1865</guid>

					<description><![CDATA[I love Mondays and Business as Usual. Solving problems is a delightful day-to-day task. Maybe this is what working with software means in the end. Do not take me wrong, it opens the doors for greenfield projects and experimentation. While mastering the business I can experiment, change and rebuild. The solving problems series is just [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I love Mondays and Business as Usual. Solving problems is a delightful day-to-day task. Maybe this is what working with software means in the end. Do not take me wrong, it opens the doors for greenfield projects and experimentation. While mastering the business I can experiment, change and rebuild.</p>
<p>The solving problems series is just a way to share small ideas, experiences and outcomes of solving daily problems as I go. I wonder if some tips or experiences shared can help you build better what you are working on right now.</p>
<hr />
<p>During the last months, <a href="https://dev.to/lpossamai/smooth-sailing-from-aws-ec2-to-ecs-a-comprehensive-migration-guide-2dci">I have been migrating</a> an important <a href="https://www.php.net/">PHP</a> service to <a href="https://aws.amazon.com/ecs/">ECS Fargate</a> along with the runtime upgrade. The service is composed of a lot of parts and we have been architecting the migration so the operation causes no downtime to customers, even when they are over four different continents and many time zones.</p>
<p>One very important part of the service is already running in production for some months with success. We are preparing the next service.</p>
<p>For the migration plan, we deployed infrastructure ahead of starting moving traffic, planned to daily incremental traffic switch, like 5, 10, 25, 50, 75, and close monitoring. Also prepared a second plan to avoid rollback in case some performance issue arises. While monitoring we created backlog tickets with the observability outcomes.</p>
<p>During migration phases prepare yourself beforehand for the initial (1%, or 5%) traffic switch, so you can catch quickly those hidden use cases that only happen in production and act quickly. If you do so, other phases are just a matter of watching how scaling works.</p>
<p>Using containers (of course Kubernetes is a great alternative) is a fantastic opportunity to upgrade PHP runtimes efficiently at the same time where we use a much better platform that helps with delivery and developer experiences. The very first and most important step I recommend is to review how you deal with your secret and environment variables. This is pivotal for the success of a smooth migration.</p>
<p>We can expect that those type of applications has a fair amount of cron jobs associated with them. This is a great opportunity to follow the old saying &quot;use the right tool for the right problem&quot; and my suggestion would be to rewrite it, turning it into <a href="https://aws.amazon.com/lambda/">Lambda</a> or <a href="https://aws.amazon.com/step-functions/">Step Functions</a>, as applicable to each of what the cron job is doing. This is closer to what and how a job should run.</p>
<p>It happens that not always we can start refactoring right away, and then I can say that my experiences with <a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/using-eventbridge-scheduler.html">Event Bridge Scheduler</a> triggering ECS tasks (previously cron jobs) are great. They are interestingly cheap alternatives while waiting for the refactoring project to take over. Don't take this as your permanent solution though, because it is not just right and a waste of resources and couple the cron job too much with parts of the application not really related.</p>
<p>We were reviewing the backlog and observability results of the last service. As we could prioritise and execute some backlog tickets, the dashboard and metrics highlighted that we had some room to review scaling and resource thresholds. We changed them carefully, resulting in a bill ~50% cheaper, CPU and memory resource stable and no performance degradation.</p>
<p>Some notes:</p>
<ul>
<li>Investing in test automation is good for your developer experience, site reliability and revenue; also a great support for technology improvements</li>
<li>It is worth taking a look at the <a href="https://docs.aws.amazon.com/autoscaling/ec2/APIReference/API_PredefinedMetricSpecification.html"><code>ALBRequestCountPerTarget</code></a> metric if you have CPU-heavy processes as you can better control how <a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-target-tracking.html">ECS will handle scale policies</a>, avoiding peak of CPU where the CPU average metric is not enough for scaling</li>
</ul>
<p>Links:</p>
<ul>
<li><a href="https://dev.to/lpossamai/smooth-sailing-from-aws-ec2-to-ecs-a-comprehensive-migration-guide-2dci">https://dev.to/lpossamai/smooth-sailing-from-aws-ec2-to-ecs-a-comprehensive-migration-guide-2dci</a></li>
<li><a href="https://www.php.net/">https://www.php.net/</a></li>
<li><a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/using-eventbridge-scheduler.html">https://docs.aws.amazon.com/eventbridge/latest/userguide/using-eventbridge-scheduler.html</a></li>
<li><a href="https://docs.aws.amazon.com/AmazonECS/latest/userguide/what-is-fargate.html">https://docs.aws.amazon.com/AmazonECS/latest/userguide/what-is-fargate.html</a></li>
<li><a href="https://aws.amazon.com/ecs/">https://aws.amazon.com/ecs/</a></li>
<li><a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-target-tracking.html">https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-target-tracking.html</a></li>
<li><a href="https://docs.aws.amazon.com/autoscaling/ec2/APIReference/API_PredefinedMetricSpecification.html">https://docs.aws.amazon.com/autoscaling/ec2/APIReference/API_PredefinedMetricSpecification.html</a></li>
<li><a href="https://aws.amazon.com/lambda/">https://aws.amazon.com/lambda/</a></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/solving-problems-1-ecs-event-bridge-scheduler-php-migrations.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1865</post-id>	</item>
	</channel>
</rss>
