<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ludovic Fleury</title>
    <description>The latest articles on Forem by Ludovic Fleury (@ludofleury).</description>
    <link>https://forem.com/ludofleury</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F221562%2F0972f725-2744-47e6-ab4a-9aed36a1bc37.jpg</url>
      <title>Forem: Ludovic Fleury</title>
      <link>https://forem.com/ludofleury</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ludofleury"/>
    <language>en</language>
    <item>
      <title>From Software Engineer to Startup CEO: What Really Changes</title>
      <dc:creator>Ludovic Fleury</dc:creator>
      <pubDate>Tue, 07 Oct 2025 14:38:25 +0000</pubDate>
      <link>https://forem.com/ludofleury/from-software-engineer-to-startup-ceo-what-really-changes-4hkk</link>
      <guid>https://forem.com/ludofleury/from-software-engineer-to-startup-ceo-what-really-changes-4hkk</guid>
      <description>&lt;p&gt;Two years ago, I took over the leadership of a growing cybersecurity startup. I had spent most of my career in startups, mostly on the tech side, as contributor and in tech management. Later on, I had few experience co-founding, failing and exiting startups. But this was the first time I stepped into the CEO role of a company already running at full speed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Moving from CTO to CEO doesn’t change what you know, it changes the weight of what you carry.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At startup scale, any C-level sees the same picture: the same information, the same constraints, the same growth target. The difference is weight.&lt;/p&gt;

&lt;p&gt;As CTO, my impact was company-wide but my leverage and burden were anchored in the “T.” I owned the tech: teams, delivery, roadmap. Everything else touched my work, but it wasn’t mine to carry.&lt;/p&gt;

&lt;p&gt;As CEO, everything connects back to me. But my true responsibility is not to do everything. &lt;strong&gt;It’s to provide the full picture: clear enough to see, strong enough to believe, shared enough to commit, and resilient enough to fail within.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a lean, flat and small organization there is no buffer. You are exposed to every micro and macro operation. The context-switching is relentless. What saved me was my past in delivery management in startups: the ability to prioritize instantly, balance urgent versus important, and keep moving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Managing director: the difference with a founder, the similarities with middle management&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s not my company. I have a board to answer to, the actual owners. In that sense, it isn’t so different from middle management, where as a VP of Engineering I reported to the C-suite.&lt;/p&gt;

&lt;p&gt;The big difference is this: the board validates (or not) my strategy, but they don’t hand me theirs. Strategy is the core of my job. They collaborate, bring insight, and challenge my thinking, but in the end they endorse my call.&lt;/p&gt;

&lt;p&gt;In that way, it’s close to “managing up” as a VP or director. You build a plan with your teams, you present it, you advocate for it. The difference is that at this level you don’t work under top-down targets. You define the targets yourself. And you own the outcome.&lt;/p&gt;

&lt;p&gt;So the job feels strangely familiar, yet heavier: you still need to commit both ways, convincing the board above and the teams below. Top-down and bottom-up at the same time.&lt;/p&gt;

&lt;p&gt;Like any role in my career, the relationship with your “bosses,” direct or indirect, matters a lot. It comes down to trust and cultural alignment. When I choose a job, I always look closely at the person above me and the culture they bring. That’s what provides the safe space to do my best work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The obsession with the why&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why does this company exist? Why does this product sell? Why do customers stay? Why do these problems repeat?&lt;/em&gt; Asking why has always been my most reliable tool: in code, in management, in strategy.&lt;/p&gt;

&lt;p&gt;I had co-founded companies before, mostly on the tech side. As a co-founder I picked up some basics in legal, contracts, HR. Those hard skills eased the workload, but they never made the difference. &lt;strong&gt;What makes a startup succeed is always the same: the value you bring to a market.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In my previous startups, that market understanding came from my business co-founders. Here, it was different. I had to ask a new set of questions: &lt;em&gt;why does this market exist? Why does it buy at this price? Why now?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My first responsibility was to trace the chain of events that brought the company here, in order to shape the strategic question that matters most: &lt;em&gt;why will this company still be relevant five years from now?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Not every why is worth chasing. Sometimes the answer changes everything. Sometimes there is no answer, and I am still learning to accept that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stepping outside the comfort zone and into the customer’s shoes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I did not know much about cybersecurity before. But throughout my career I have loved jumping into new domains: retail, e-commerce, social, payment, education, music, AI. That is one of the privileges of software engineering. You can keep coding in your familiar stack while solving entirely new problems and delivering completely different solutions.&lt;/p&gt;

&lt;p&gt;The sales motion also felt familiar. Most of our growth came inbound, through digital channels I already understood as a builder: SEO, social, community. The team was mostly software engineers, echoing my past responsibilities. And the company was still in its earliest years, a stage I know best.&lt;/p&gt;

&lt;p&gt;As a former tech, the product we offer is something I could imagine buying myself. In a way, I moved from being in the customer’s shoes to being the provider. I swear by &lt;a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food" rel="noopener noreferrer"&gt;dogfooding&lt;/a&gt;. Seeing your product through the eyes of a real customer is the most authentic driver of a truly customer-centric culture.&lt;/p&gt;

&lt;p&gt;The big question then became: &lt;em&gt;why didn’t I buy this product before?&lt;/em&gt; Some of those answers became the foundation of my strategy. But you also have to challenge it with another question: &lt;em&gt;why am I not the target?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observability, business-wide&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To ask the right questions, I first needed the right ground truth. I spent several months digging to understand everything. Launching a startup is overwhelming, and documenting every decision is usually overlooked. But here, the founding team had been meticulous. A company built by engineers tends to put observability at its core, and it showed.&lt;/p&gt;

&lt;p&gt;It wasn’t about fancy dashboards or over-engineered KPI frameworks. It was about raw data of what was done, when, how and how much. The kind of ground truth that makes it possible to really measure a business.&lt;/p&gt;

&lt;p&gt;Observability is more than tools and data. &lt;strong&gt;Data without a story is noise. A story without data is fiction.&lt;/strong&gt; The real value lies in interpretation, and that goes far beyond dashboards or metrics.&lt;/p&gt;

&lt;p&gt;It lives in meeting notes, in memos, and ultimately in the people who experienced those datapoints. You have to ask them “why,” listen to their interpretation, align the stories, and surface the conflicts. That is where observability becomes understanding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Velocity and the feedback loop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the first principles I learned in developer experience, I think it was from Basecamp or GitHub, was simple: &lt;strong&gt;the speed of the feedback loop defines performance&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That idea stuck with me. I became obsessed with shortening every loop: local setup time, CI execution, branching, release cycles. Later it became onboarding time, and how quickly a team could pivot.&lt;/p&gt;

&lt;p&gt;The same principle drives me today, just on a wider scope. The units of measurement have changed. Sometimes they are quarters or years instead of minutes or hours. But the strategy is the same: reduce cycles from months to weeks, operations from hours to seconds.&lt;/p&gt;

&lt;p&gt;Sales cycles, support response times, customer onboarding, releases, incident resolution. The faster the loop, the faster the company iterates. Speed not only makes a business more resilient, it makes the product more valuable. &lt;strong&gt;In many markets, speed is the feature.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ask for help, from the right people&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Stepping into new areas like marketing and sales was one of the biggest challenges. In management that usually means hiring the right talent. But when you have zero experience in a field, how do you even define talent? The whole logic of skill authority I used in tech did not translate directly.&lt;/p&gt;

&lt;p&gt;Or maybe it did, just differently. I realized I already knew people who were true experts, not because of their followers or their books sales, but because they were operational, in the field, grounded in the day-to-day reality of building. That context made their insight invaluable because it connected to mine.&lt;/p&gt;

&lt;p&gt;I cannot thank enough those who picked up my calls, answered my emails, replied to a LinkedIn message, shared a lunch, or a few minutes over coffee. People in marketing, in sales. Some even helped me qualify resumes. I owe them, big time.&lt;/p&gt;

&lt;p&gt;For everything else, it’s not &lt;em&gt;that&lt;/em&gt; different from engineering. Marketing is a pipeline. Sales is a pipeline too. Of course, they’re very different kinds of pipelines, but conceptually the logic is the same: something goes in, and if the system works, something valuable comes out.&lt;/p&gt;

&lt;p&gt;The same principles apply: observability, acting on information, iterating. The actions themselves are different, but that’s where talent comes in. It’s no different than in engineering management. As a manager, I would hire someone prolific in a technology I understood conceptually, but without being hands-on myself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance is a culture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That has not changed. I have always believed performance is cultural. The difference now is that I joined a company already performing. That meant its culture was working. The real task was not to fix it, but to understand why it worked, and why some changes were still needed.&lt;/p&gt;

&lt;p&gt;It is about continuity, not perpetuity. In engineering we talk about continuous integration and continuous deployment, frameworks that enable fast change. It is the same here. The goal is to build a company that embraces change, not one that changes everything for the sake of it. Like engineers say: “if it ain’t broken, don’t fix it.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Software engineering and tech management gave me great tools to onboard into this new adventure. The &lt;a href="https://agilemanifesto.org/" rel="noopener noreferrer"&gt;agile manifesto&lt;/a&gt; is not about code, it's about value. That’s why its principles of iteration and collaboration apply just as well outside engineering.&lt;/p&gt;

&lt;p&gt;Of course, it has its biases. But the key is to stay aware of them. And sometimes the most powerful thing you can say as a leader is simply: “I don’t know.” That’s not weakness. It’s awareness and honesty.&lt;/p&gt;

</description>
      <category>career</category>
      <category>careerdevelopment</category>
      <category>startup</category>
    </item>
    <item>
      <title>HTTP API Lean Workflow: from contract to documentation</title>
      <dc:creator>Ludovic Fleury</dc:creator>
      <pubDate>Fri, 02 Jul 2021 18:04:23 +0000</pubDate>
      <link>https://forem.com/golem-ai/http-api-lean-workflow-from-contract-to-documentation-20m7</link>
      <guid>https://forem.com/golem-ai/http-api-lean-workflow-from-contract-to-documentation-20m7</guid>
      <description>&lt;p&gt;How do we rely on open source software to achieve collaborative API design and produce our &lt;a href="https://developers.golem.ai/docuchecker" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;?&lt;/p&gt;

&lt;h2&gt;
  
  
  TL:DR;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A contract-first approach enables high collaboration and removes overhead.&lt;/li&gt;
&lt;li&gt;Versioning contracts with the code since they share the same lifecycle.&lt;/li&gt;
&lt;li&gt;Stoplight.io DX is a powerful API IDE for OpenAPI.&lt;/li&gt;
&lt;li&gt;Enriching the OpenAPI contract with markdown reduces the cost of maintenance. &lt;/li&gt;
&lt;li&gt;Rely on Redoc to expose the contract as documentation with a standard UX &amp;amp; UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;At &lt;a href="https://golem.ai/" rel="noopener noreferrer"&gt;Golem.ai&lt;/a&gt;, we are maintaining a platform and several services following the &lt;a href="https://en.wikipedia.org/wiki/Service-oriented_architecture" rel="noopener noreferrer"&gt;SOA&lt;/a&gt; concepts. Our protocol between these services relies on &lt;a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm" rel="noopener noreferrer"&gt;REST&lt;/a&gt; API &lt;a href="https://martinfowler.com/articles/richardsonMaturityModel.html#level2" rel="noopener noreferrer"&gt;level 2&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;These API are either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;consumed programmatically (internally and by our customers)&lt;/li&gt;
&lt;li&gt;used by several web applications (addressing both our business analysts and customers)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://openpracticelibrary.com/practice/contract-first-development/" rel="noopener noreferrer"&gt;Contract-first approach&lt;/a&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;There are only two hard things in Computer Science: cache invalidation and naming things.&lt;br&gt;
-- Phil Karlton&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Designing API is a tough job. It's a highly structuring action where we define the boundary of "something". These boundaries exposes our knowledge and understanding about this "something": a service, a micro-service, a bounded context.&lt;/p&gt;

&lt;p&gt;At Golem.ai, API design is not only about decision, responsibility &amp;amp; technicality. We advocate continuous collaboration to guarantee the quality &amp;amp; value of our APIs.&lt;/p&gt;

&lt;p&gt;Adopting the &lt;a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food" rel="noopener noreferrer"&gt;dogfooding philosophy&lt;/a&gt;, we consume our own API. For every new feature, fix or improvement that impacts an endpoint, we follow this 3-step process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Discovery&lt;/strong&gt;: provider &amp;amp; consumer (example: backend &amp;amp; frontend) engineers sit together and discuss their technical challenges. Exploring code, questioning the &lt;a href="https://basecamp.com/shapeup/webbook" rel="noopener noreferrer"&gt;shape (specification)&lt;/a&gt; and referring to the &lt;a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm" rel="noopener noreferrer"&gt;REST approach&lt;/a&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Agreement&lt;/strong&gt;: ultimately, engineers will come out with a pragmatic and relevant solution. They update with Stoplight.io and version the &lt;a href="https://www.openapis.org/" rel="noopener noreferrer"&gt;OpenAPI contract&lt;/a&gt; with git. Usually, this contract has been drafted during the discovery by both parties.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;: &lt;a href="https://en.wikipedia.org/wiki/Test-driven_development#:~:text=Test%2Ddriven%20development%20(TDD),software%20against%20all%20test%20cases." rel="noopener noreferrer"&gt;TDD&lt;/a&gt; is cheaper, our OpenAPI contracts include examples that provide  a free mock for the client and free test assertion &amp;amp; server-side validation. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Adjustment: nothing is perfect and near perfection takes time and iteration. Developers can adjust an endpoint but they have to go again through steps 1, 2 &amp;amp; 3. Hopefully, adjustments are usually small and the whole process is short, 5 minutes tops: a slack message, a meeting using screen sharing and a quick discussion will lead to a git commit updating the OpenAPI contract.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Contract as Documentation
&lt;/h2&gt;

&lt;p&gt;We needed more than a contract or a reference to on-board, assist, and guide developers using our API. &lt;/p&gt;

&lt;p&gt;Documentation needs maintenance and its lifecycle is tightly bound to the implementation. &lt;/p&gt;

&lt;p&gt;After assessing solutions on the market, we realised that most of the option would either involve changing our workflow or adding complexity to sync the documentation. &lt;/p&gt;

&lt;p&gt;We finally decided to use our OpenAPI contract as documentation with Redoc (read more below). &lt;/p&gt;

&lt;p&gt;The final workflow is simple, involving only the OpenAPI contract, versioned with the code. Our regular pipeline deploy the code and the documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Side effects&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The code review validates everything: contract, code &amp;amp; documentation. &lt;/li&gt;
&lt;li&gt;We didn't increase the complexity and the cost of our API.&lt;/li&gt;
&lt;li&gt;Everything, from modelling to documenting, is tied to the lifecycle of the code: ideation, testing, release &amp;amp; deployment. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We ended up maximizing the value of every step of our API management, reducing the overhead for our developers, and removing conflict during integration. &lt;strong&gt;Collaboration versioned in an OpenAPI contract ensure predictable, meaningful, and enjoyable API development.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools: our API Stack
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Stoplight.io
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://stoplight.io/" rel="noopener noreferrer"&gt;Stoplight.io&lt;/a&gt; is a game changing tool when it comes to managing &amp;amp; editing OpenAPI contracts.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We use the desktop application, so we can locally load our contract from our git repository into the editor.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next level &lt;a href="https://medium.com/swlh/what-is-dx-developer-experience-401a0e44a9d9" rel="noopener noreferrer"&gt;DX&lt;/a&gt;: none of our developers were familiar with OpenAPI and they didn’t  have to be with stoplight: WYSIWYG for discovery, linter, direct edition in yaml/json, preview... Engineers can focus on the "what" &amp;amp; "why" rather than the "how".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not intrusive: the desktop app lets you define your own convention, standard, directory structure, and workflow. It adapts to your needs and your organization doesn't have to fit the tool.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provides a mock server locally. We can even quickly prototype our implementation: our local docker container fetches generated data complying to the defined contract.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Redoc
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/Redocly/redoc" rel="noopener noreferrer"&gt;Redoc&lt;/a&gt; is a fantastic &lt;a href="https://en.wikipedia.org/wiki/Do_it_yourself" rel="noopener noreferrer"&gt;DIY&lt;/a&gt; tool, it leverages the markdown syntax and the OpenAPI description fields to enrich your contract. Basically, &lt;strong&gt;your contract is not only a reference, but becomes your documentation&lt;/strong&gt;, example: &lt;a href="https://developers.golem.ai/docuchecker#section/Getting-started" rel="noopener noreferrer"&gt;this getting started&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As we maintained and documented several services, we needed to aggregate all our contracts/documentations under a single url (developers.golem.ai). This &lt;a href="https://github.com/volbrene/redoc" rel="noopener noreferrer"&gt;Fork&lt;/a&gt; by &lt;a href="https://www.rene-volbach.de/" rel="noopener noreferrer"&gt;Volbrene&lt;/a&gt; solved this problem for us.&lt;/p&gt;

&lt;p&gt;Once again, the &lt;a href="https://stoplight.io/studio/" rel="noopener noreferrer"&gt;stoplight studio desktop app&lt;/a&gt; is bliss, offering markdown edition &amp;amp; preview:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Git &amp;amp; Gitlab perk
&lt;/h3&gt;

&lt;p&gt;API contracts are versioned in the same git repository as the related base code at &lt;code&gt;/doc/OpenAPI&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Gitlab offers a &lt;a href="https://docs.gitlab.com/ee/user/project/repository/#openapi-viewer" rel="noopener noreferrer"&gt;nice perk based on a naming convention&lt;/a&gt;. Adding "openapi" in your contract file name enables a swagger-like preview in the gitlab code explorer.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Our engineering team is hiring in Paris, France. php/Symfony junior, contact us!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Many thanks to Azure: if you need proofreading contact her at &lt;a href="mailto:heatherklamb@gmail.com"&gt;heatherklamb@gmail.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>rest</category>
      <category>documentation</category>
      <category>openapi</category>
    </item>
    <item>
      <title>Domain Driven Design with PHP and Symfony</title>
      <dc:creator>Ludovic Fleury</dc:creator>
      <pubDate>Tue, 02 Jun 2020 03:21:26 +0000</pubDate>
      <link>https://forem.com/ludofleury/domain-driven-design-with-php-and-symfony-1bl6</link>
      <guid>https://forem.com/ludofleury/domain-driven-design-with-php-and-symfony-1bl6</guid>
      <description>&lt;p&gt;Personal take on Domain Driven Design with &lt;a href="https://www.php.net/" rel="noopener noreferrer"&gt;PHP&lt;/a&gt; and &lt;a href="https://symfony.com/" rel="noopener noreferrer"&gt;Symfony framework&lt;/a&gt; &lt;em&gt;(2.x to 5.x)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was written &lt;a href="https://dev.to/ludofleury/domain-driven-design-avec-php-symfony-1p2h"&gt;in french here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Meaning is everything
&lt;/h1&gt;

&lt;p&gt;DDD is gaining momentum in the Symfony &amp;amp; PHP community. I'm committed into a &lt;a href="https://dev.to/ludofleury/big-ball-of-mud-1130"&gt;quest for "meaning"&lt;/a&gt; and so I'm a DDD evangelist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Definition
&lt;/h2&gt;

&lt;p&gt;DDD is a paradigm, an approach, a way.&lt;br&gt;
Take a moment to reflect on the acronym because in order to get &lt;strong&gt;the pure value of DDD, you only need to understand these 3 words together: "Domain" "Driven Design"&lt;/strong&gt;  If you need to remember only one thing from DDD, it should be its name.&lt;/p&gt;

&lt;p&gt;DDD isn't an software architecture or architectural style.  DDD isn't about Entity, Value Object or Aggregate Root. They are convenient, and misleading, technicality in the object-oriented world.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DDD in essence is a shared understanding of the business domain crystallised in a base code by developers.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  In practice
&lt;/h2&gt;

&lt;p&gt;If business people would take the time to read a DDD code repository, they should understand 80% of it. Actually, &lt;a href="https://en.wikipedia.org/wiki/High-level_programming_language" rel="noopener noreferrer"&gt;high level language&lt;/a&gt; like PHP could be considered like a really poor subset of the english language. With DDD, the PHP code describe business rules, requirements, invariants in an "object-oriented english".&lt;/p&gt;

&lt;p&gt;How does my code look like? Business domain processes described with a poor english:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$cashier&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$cashier&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$card&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$accountant&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$compliance&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How does my classes look like? Business concepts written in a poor english:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Legal\Company\France&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * French unique registration number for a specific establishments of a company
 * issued by INSEE
 * SIRET: "Système d'Identification du Répertoire des ETablissements"
 * 
 * @see https://www.economie.gouv.fr/entreprises/numeros-siren-siret
 */&lt;/span&gt;
&lt;span class="kd"&gt;Class&lt;/span&gt; &lt;span class="nc"&gt;Siret&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;RegistrationNumber&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;Siren&lt;/span&gt; &lt;span class="nv"&gt;$siren&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;Nic&lt;/span&gt; &lt;span class="nv"&gt;$nic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;siren&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Siren&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;mb_substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Nic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;mb_substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Exception&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\DomainException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Invalid SIRET number'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="err"&gt;  &lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getNumber&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;siren&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getNumber&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nic&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getNumber&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Legal\Company\France&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * Unique registration number for a french company
 * Issued by INSEE, composing the first part of SIRET
 * Système d'Identification du Répertoire des ENtreprises
 *
 * @see https://bpifrance-creation.fr/encyclopedie/formalites-creation-dune-entreprise/formalites-generalites/numeros-didentification
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Siren&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/^[0-9]{9}$/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\DomainException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Invalid SIREN number'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;

         &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getNumber&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;French developers might be familiar with the concepts examplified. However, if you aren't, it shouldn't take you too long to get a clear idea about the rules governing the french company registration numbers... simply by reading the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The direct benefits
&lt;/h2&gt;

&lt;p&gt;More than readable code, we have &lt;strong&gt;&lt;a href="https://medium.com/@daniel.oliver.king/writing-expressive-code-b69ef7a5a2fa#:~:text=This%20is%2C%20I%20think%2C%20one,human%20who%20reads%20our%20code." rel="noopener noreferrer"&gt;expressive&lt;/a&gt; code&lt;/strong&gt; translating literraly the business concepts  &amp;amp; domain.&lt;/p&gt;

&lt;p&gt;The code is also "&lt;a href="https://en.wikipedia.org/wiki/Immutable_object" rel="noopener noreferrer"&gt;immutable&lt;/a&gt;".&lt;br&gt;
I didn't follow a best practice or a specific pattern, I just followed the french registration number rules. These numbers are immutable, so my code is immutable... and so these classes naturally fit the Value Object definition. It wasn't the goal of the implementation, it's a side effect of it.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;a href="https://www.youtube.com/watch?v=Wi5_0fNLSIQ" rel="noopener noreferrer"&gt;Free your mind&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Forget about "best practice", "design pattern", "fluent interface", "event dispatching", , MVC, SoC, SRP, SOA, RAD, CRUD, DRY... &lt;br&gt;
&lt;strong&gt;Forget tech, Domain drives your design, domain comes first.&lt;/strong&gt; Use your language (here PHP) to model the domain in the simplest possible way. Like a business expert will try to teach a toddler about a process, the devs are trying to make a machine process it while keeping the meaning explicit.&lt;/p&gt;

&lt;p&gt;If you are using Entities with setter &amp;amp; getter, some Value Object in the &lt;code&gt;/ValueObject/&lt;/code&gt; namespace, you are missing the essence of DDD.&lt;/p&gt;

&lt;p&gt;Again, DDD is not about respecting a specific way to implement stuff. DDD is about sharing the language between the code and the business domain.&lt;/p&gt;

&lt;p&gt;Another value of DDD: &lt;strong&gt;the code has value at runtime AND at rest.&lt;/strong&gt; The code is self-documenting a business process, acting as knowledge center for any human interacting with it.&lt;/p&gt;
&lt;h1&gt;
  
  
  DDD and Symfony
&lt;/h1&gt;

&lt;p&gt;I started to read &amp;amp; implement DDD around 2014.&lt;br&gt;
It took me a full year reading &amp;amp; implementing before I start to feel comfortable with my tooling (PHP/Symfony/Doctrine) and DDD. As &lt;a href="https://twitter.com/ericevans0" rel="noopener noreferrer"&gt;Evans&lt;/a&gt; said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Don't fight your framework".&lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215" rel="noopener noreferrer"&gt;Domain-driven Design: Tackling Complexity in the Heart of Software&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What is my "pragmatic" implementation of DDD with Symfony?&lt;br&gt;
It really depends of the project &amp;amp; the complexity. But when I'm bootstrapping a new project or coaching a new dev, I usually rely on these simple guidelines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I heavily rely on the &lt;a href="https://olvlvl.com/2018-04-command-dispatcher-pattern" rel="noopener noreferrer"&gt;command pattern&lt;/a&gt;, &lt;a href="https://symfony.com/doc/current/components/messenger.html" rel="noopener noreferrer"&gt;Symfony Messenger&lt;/a&gt; is a bliss&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I do not use &lt;a href="https://martinfowler.com/bliki/CQRS.html" rel="noopener noreferrer"&gt;CQRS&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I do not use &lt;a href="https://martinfowler.com/eaaDev/EventSourcing.html" rel="noopener noreferrer"&gt;ES&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I use  &lt;a href="https://github.com/Behat/Behat" rel="noopener noreferrer"&gt;Behat&lt;/a&gt; &amp;amp; &lt;a href="https://cucumber.io/docs/bdd/" rel="noopener noreferrer"&gt;BDD&lt;/a&gt; at the Command/Handler level, full coverage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I rarely use unit testing in project, 20-30% code coverage usually.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Why no CQRS &amp;amp; ES?
&lt;/h2&gt;

&lt;p&gt;CQRS &amp;amp; ES are costly. They are extremely powerful solutions but you should use them only when it makes sense cost-wise &amp;amp; business-wise.&lt;/p&gt;
&lt;h3&gt;
  
  
  ES
&lt;/h3&gt;

&lt;p&gt;Not so many php devs are used to Event Sourcing, and you won't find a lot able to design or use an Event Store properly. If you do not have a good model maturity remember that your events store directly reflects the history of your mistake, which might drastically increase the maintainability cost.&lt;/p&gt;
&lt;h3&gt;
  
  
  CQRS
&lt;/h3&gt;

&lt;p&gt;CQRS truly shines at scale. This usually brings &lt;a href="https://stackoverflow.com/questions/10078540/eventual-consistency-in-plain-english" rel="noopener noreferrer"&gt;eventual consistency&lt;/a&gt; specificity between the write &amp;amp; read models. Do you really need this complexity and cost?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I implemented both patterns in different projects with PHP/Symfony, sometimes Doctrine, SQL &amp;amp; MongoDB&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  src structure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;src/Controller/&lt;/code&gt;: Symfony controller&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;src/Command/&lt;/code&gt;: Symfony CLI commands&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;src/Action/&lt;/code&gt;: Command + CommandHandler &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;src/Domain/&lt;/code&gt;: AR, Entity, VO, Repository Interface&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;src/*&lt;/code&gt;: Infrastructure, other stuff.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having something called &lt;code&gt;/Domain/&lt;/code&gt; sucks. But it also works.&lt;br&gt;
Usually I replace "Domain" by the actual Domain we are tackling like &lt;code&gt;/Ecommerce/&lt;/code&gt;. &lt;/p&gt;
&lt;h3&gt;
  
  
  Multiple domains issue
&lt;/h3&gt;

&lt;p&gt;The problem starts when you have multiple domains. Opinions are quite strong about Core &amp;amp; supporting domain. Me? I don't care. I put everything in &lt;code&gt;/Domain/&lt;/code&gt; and then split if needed without qualifying as "Core" or "Supporting":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/Domain/Payment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/Domain/Compliance&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/Domain/Delivery&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's simple &amp;amp; it's consistent.&lt;/p&gt;
&lt;h3&gt;
  
  
  What's in the "Domain“ ?
&lt;/h3&gt;

&lt;p&gt;Inside the &lt;code&gt;src/Domain/&lt;/code&gt;, I put every class that implement a business domain concern/concept. Some are persisted classes, others aren't, we also have the domain service there.&lt;/p&gt;

&lt;p&gt;Since &lt;code&gt;src/Entity&lt;/code&gt; doesn't exist anymore, Doctrine configuration is customised to scan &lt;code&gt;src/Domain&lt;/code&gt;. Annotations are used to configure the persistence mapping, because "locality" is great.&lt;/p&gt;

&lt;p&gt;Obviously when the project gets bigger, I do reorganize the &lt;code&gt;/Domain/&lt;/code&gt; in order to reflect the business domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/Domain/Legal/Company/France/RegistrationNumber/Siret.php
/Domain/Legal/Company/France/RegistrationNumber/Siren.php
/Domain/Legal/Company/France/RegistrationNumber/Nic.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a href="https://youtu.be/3HUWUtTZvK4?t=10" rel="noopener noreferrer"&gt;Command to conquer&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The good
&lt;/h3&gt;

&lt;p&gt;I love the command pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;command handlers play well with &lt;a href="https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)" rel="noopener noreferrer"&gt;hexagonal architecture&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;command handlers bring consistency to manipulate my model layer, they are the unique entrypoints.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;command handlers bring consistency to the application service layer, as they are the application service&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;command handlers define the transactionnal boundaries of a business process/action&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;command handlers help to deal &amp;amp; design with Aggregate Root&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The bad and the ugly
&lt;/h3&gt;

&lt;p&gt;I do abuse command handlers in some circumstances: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When facing unknown specs, lack of understanding, lack of available knowledge, when I'm lazy or in a rush: I implement in the command handler code that would be moved later into domain services or domain objects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When I need to synchronize several business processes together, I use handler instead of the &lt;a href="https://blog.couchbase.com/saga-pattern-implement-business-transactions-using-microservices-part/#:~:text=The%20SAGA%20Pattern,data%20within%20a%20single%20service." rel="noopener noreferrer"&gt;SAGA&lt;/a&gt; hell&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When an AR implementation is really tricky or costly with Doctrine/Symfony. I do leak the implementation in the handler (it's bad, don't do this at home)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;It sounds probably terrible for purist. I'm really sorry and really okay with that as it worked for me.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Naming the command
&lt;/h3&gt;

&lt;p&gt;Commands are named against the process, the business action or behavior they&lt;br&gt;
implement. I do adapt to reflect the specific business needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;src/Action/Checkout.php
src/Action/CheckoutHandler.php

src/Action/FraudulentPaymentDeclaration.php
src/Action/FraudulentPaymentDeclarationHandler.php

src/Action/Compliance/Registration.php
src/Action/Compliance/RegistrationHandler.php
src/Action/Compliance/FrenchEstablishmentRegistration.php
src/Action/Compliance/FrenchEstablishmentRegistrationHandler.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Maybe I would have used an imperative form for the Command: Register instead of Registration&lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://twitter.com/docteur_klein/status/1267008375646302214?s=20" rel="noopener noreferrer"&gt;Florian Klein&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Business process, domain process or domain action are usually called by a unique name. It is important to tak the time to identify exactly the noun used to qualify this process. It's not always possible.&lt;/p&gt;

&lt;p&gt;A verb might be a symptom of a weak domain understanding, example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MarkAsRead&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ValidateStatus&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's an advice, a best effort. In reality, in my repo I do have a lot of command phrased with a verb.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation details
&lt;/h3&gt;

&lt;p&gt;Commands are expression of (domain) intents. They are designed as simple &lt;a href="https://en.wikipedia.org/wiki/Data_transfer_object" rel="noopener noreferrer"&gt;DTO&lt;/a&gt; accepting only  &lt;a href="https://en.wikipedia.org/wiki/Language_primitive#High_level_language_primitives" rel="noopener noreferrer"&gt;primitives&lt;/a&gt;&lt;br&gt;&lt;br&gt;
 easing hexagonal architecture &amp;amp; [port/adapter](&lt;a href="https://matthiasnoback.nl/2017/08/layers-ports-and-adapters-part-3-ports-and-adapters/" rel="noopener noreferrer"&gt;port/adapter&lt;/a&gt; implementation. Commands are the boundary between primitives and domain concepts in my application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Action\Compliance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Domain\Geo\Address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Domain\Geo\Country&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Registration&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressLine1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressLine2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressZip&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressLocality&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressCountry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressLine1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressLine2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressZip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressLocality&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressCountry&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;
    &lt;span class="c1"&gt;// ... boring stuff&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Uuid&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uuid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getAddress&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Address&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addressLine1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addressLine2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addressZip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addressLocality&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addressCountry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I do add Symfony validation with annotation to my command in order to pre-validate and dispatch to the user any input errors. Yet when it comes to enforce business rules: my domain concept (mostly Value Object) carry the implementation and any violation raise exception.&lt;/p&gt;

&lt;p&gt;Implementing commands this way, ease hexagonal: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;commands can be used as a &lt;a href="https://symfony.com/doc/current/form/use_empty_data.html" rel="noopener noreferrer"&gt;Symfony/Form model&lt;/a&gt; you know: the "data-class".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;commands can be composed from arguments of a Symfony/Console CLI command.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;commands can be composed by a RabbitMQ worker/consumer from a unserialized message&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;command can be composed by an event processed from a Symfony/EvenetListener&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;from web socket, etc...&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once my command is passed to its handler, we enter the domain kingdom. Only dmain object are used over there (VO, Entity). A domain object provides strong guarantees:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;its state is consistent, always valid (according to the business rules)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;its state is mostly immutable&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;its public API allows only expected states change, following the domain specification&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using these domain object is a delight and a relief: the &lt;a href="https://en.wikipedia.org/wiki/Cognitive_load" rel="noopener noreferrer"&gt;cognitive load&lt;/a&gt; is really low and the confidence in the system is very high.&lt;/p&gt;

&lt;p&gt;Few tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I do not use typed ID for each AR/Entity (UserId, BookId). It's a pain to maintain with Doctrine. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When to use a VO (domain object) over a primitive? If the concept is important/relevant for the domain, if the concept follows rules from the domain: it's a VO. Otherwise, primitives are good enough. We're still in PHP, not in Java.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Command handlers ?
&lt;/h3&gt;

&lt;p&gt;Command handlers coordinate the domain concept altogether (VO, Entity, AR).&lt;/p&gt;

&lt;p&gt;It usually relies on a repository to fetch the concerned AR.&lt;/p&gt;

&lt;p&gt;The handler then calls methods on the domain object (AR, Entity). Since Doctrine is following &lt;a href="https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/tutorials/getting-started.html#what-is-doctrine" rel="noopener noreferrer"&gt;data-mapping&lt;/a&gt;, the handler manage the persistence cycle. Somtimes it coordinate other infrastructure details, usually anything related to IO required to compose a correct state in order to perform a business action.&lt;/p&gt;

&lt;p&gt;Symfony Messenger does the heavy lifting. You just need to &lt;a href="https://symfony.com/doc/current/components/messenger.html#bus" rel="noopener noreferrer"&gt;dispatch the command into the bus&lt;/a&gt;. from your CLI-command, HTTP controller, RabbitMQ consumer and voila, your handler will be invoked with the command.&lt;/p&gt;

&lt;p&gt;Since I do not use CQRS, most of my commands are synchronous over HTTP. Using Symfony/Messenger &lt;a href="https://symfony.com/doc/current/messenger/handler_results.html" rel="noopener noreferrer"&gt;to get the last result of a command execution&lt;/a&gt;, I just need to add some serialization to get a &lt;a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm" rel="noopener noreferrer"&gt;REST&lt;/a&gt; representation for the HTTP response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bounded Contexts (BC)
&lt;/h2&gt;

&lt;p&gt;I consider one git repo as one bounded context. My understanding might &amp;amp; will change later. I could split BC's later.&lt;/p&gt;

&lt;p&gt;A lot of BC's might mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;domain misunderstanding&lt;/li&gt;
&lt;li&gt;unknown about the domain&lt;/li&gt;
&lt;li&gt;high complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Organizing BC's
&lt;/h2&gt;

&lt;p&gt;Microservice are a  pain to orchestrate. If you've got a devops army good for you. If not, locality works again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/BC1/Controller/
src/BC1/Command/
src/BC1/Domain/

src/BC2/Controller/
src/BC2/Command/
src/BC2/Domain/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why? Because splitting BC is risky. Synchronising BC is costly. Your understanding will develop and will require a lot of refactoring &amp;amp; iteration: moving code from "BC's" and "domains" back and forth. Once you reach model maturity, split your repo, do your microservice show.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;These are tricks or compromises that I found efficient to standardise a DDD approach with PHP, Symfony &amp;amp; Doctrine. These are not what you should focus on. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;These are what you might wanna use to never have to think about the "how" and just focus about the "what" &amp;amp; "why".&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Bonus track
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Can I share/re-use concepts over BC ?
&lt;/h2&gt;

&lt;p&gt;First: Never ever share Entities across BC.&lt;br&gt;
Second: VO sharing is tough, especially with Doctrine, you would need to add your domain via composer/vendor and add mapping via yaml.&lt;br&gt;
Last: Domain sharing is so-so. Like you can share a Geo domain (ISO Country, ISO Phone, ISO Currency)... but that would be very generic supporting domain.&lt;/p&gt;

&lt;p&gt;Why "core" domain sharing is bad? Because from 2 different BC's if a domain is exactly seen &amp;amp; envisioned with the same properties, rules, shapes... Well you have only one BC. Careful, it's not because 2 BC shares domain names that they mean the same thing.&lt;/p&gt;

&lt;p&gt;Example: For HR dept(BC) and Tech management(BC), "Employee" (Domain) means something very different even if they share some similitude.&lt;/p&gt;

&lt;h2&gt;
  
  
  BC collaboration, synchronization?
&lt;/h2&gt;

&lt;p&gt;This is where things get hairy. First of all, BC should be isolated, you shouldn't have any direct coupling.&lt;/p&gt;

&lt;p&gt;Data-wise, either you go for 2 storages or 2 schemas if you're in SQL.&lt;br&gt;
if you are cheap and share the same schema... do not make relation between your BC's tables.&lt;/p&gt;

&lt;p&gt;The command pattern is really helpful there. Let say I have a checkout process with a BC about payment and another BC about compliance.&lt;/p&gt;

&lt;p&gt;Lazy: I can call my 2 commands PaymentCharge &amp;amp; PaymentFraudAnalyze at the same level (HTTP controller, CLI-command). Pro: It guarantee the loose coupling. Cons: your controller/cli-command carry some app/service/business sync.&lt;/p&gt;

&lt;p&gt;Best: Go for event. Use the Symfony dispatcher, don't build your own stuff. Explicitly dispatch an event from your Command Handler. Create a specific EventListener in BC2. DON'T be lazy, do not go for "command sourcing pattern". Your BC1 is emitting an event ("Mom I'm Done"), your BC2 is listening, then transforming to its own command.&lt;/p&gt;

&lt;p&gt;You can also add static analysis to enforce coupling rules (ensure BC aren't mixed). If you keep BC isolated (Data storage) &amp;amp; sync BC's with event, you are building scalable BC's respecting microservice requirements. Meaning, you could quickly move and split your BCs across repo and your infra.&lt;/p&gt;

</description>
      <category>ddd</category>
      <category>php</category>
      <category>symfony</category>
      <category>programming</category>
    </item>
    <item>
      <title>Domain Driven Design avec PHP &amp; Symfony</title>
      <dc:creator>Ludovic Fleury</dc:creator>
      <pubDate>Sun, 31 May 2020 16:00:56 +0000</pubDate>
      <link>https://forem.com/ludofleury/domain-driven-design-avec-php-symfony-1p2h</link>
      <guid>https://forem.com/ludofleury/domain-driven-design-avec-php-symfony-1p2h</guid>
      <description>&lt;p&gt;&lt;em&gt;english version &lt;a href="https://dev.to/ludofleury/domain-driven-design-with-php-and-symfony-1bl6"&gt;available here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Partage d'observations, considérations &amp;amp; recommandations concernant les développements pilotés par le métier avec &lt;a href="https://www.php.net/" rel="noopener noreferrer"&gt;PHP&lt;/a&gt; et le framework &lt;a href="https://symfony.com/" rel="noopener noreferrer"&gt;Symfony&lt;/a&gt; (version 2.x à 5.x)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;note: pas l'habitude de rédiger en français sur les sujets techniques. Les sources, liens &amp;amp; références seront souvent en anglais&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Redonner du sens au développement
&lt;/h1&gt;

&lt;p&gt;DDD gagne en popularité dans la communauté Symfony/PHP. J'évoquais précédemment &lt;a href="https://dev.to/ludofleury/big-ball-of-mud-1130"&gt;ma quête insatiable de sens&lt;/a&gt; qui motive mon évangélisation toute particulière pour le DDD.&lt;/p&gt;

&lt;h2&gt;
  
  
  Définition
&lt;/h2&gt;

&lt;p&gt;DDD c'est un &lt;a href="https://fr.wikipedia.org/wiki/Paradigme#:~:text=Un%20paradigme%20est%20%E2%80%94%20en%20%C3%A9pist%C3%A9mologie,th%C3%A9orique%2C%20courant%20de%20pens%C3%A9e" rel="noopener noreferrer"&gt;paradigme&lt;/a&gt;, une approche, une méthode, une façon de penser et de voir. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La valeur du DDD réside dans sa définition: "développement piloté par le métier"&lt;/strong&gt;. Si vous ne devez retenir et comprendre qu'une seule chose du DDD: c'est bien son intitulé. Si vous restez concentré sur le métier, le contexte métier, les règles métiers, le vocabulaire métiers, les acteurs métiers dès que vous lancez votre IDE pour coder: votre code sera en accord avec les principes DDD.&lt;/p&gt;

&lt;p&gt;Le &lt;strong&gt;DDD n'est pas une architecture logicielle&lt;/strong&gt; ou un style architectural. Les "Entities", les "Value Objects" ou les "Aggregates Root" ne sont que des détails techniques trompeurs, uniquement utiles dans nos univers orientés objets. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Le DDD c'est essentiellement une compréhension du métier cristallisée dans un dépôt de code par les développeurs&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concrètement
&lt;/h2&gt;

&lt;p&gt;Si les chefs de projets, les "stakeholders" ou les responsables métiers prenaient le temps de lire le contenu d'un dépôt DDD, ils devraient normalement comprendre 80% du code. &lt;/p&gt;

&lt;p&gt;En réalité les &lt;a href="https://en.wikipedia.org/wiki/High-level_programming_language" rel="noopener noreferrer"&gt;langages de programmation de haut niveau&lt;/a&gt; comme PHP peuvent être considérés comme une version extrêmement  appauvrie de l'anglais. En DDD, le code PHP décrit une règle métier ou un concept métier en "anglais orienté-objet".&lt;/p&gt;

&lt;p&gt;A quoi ressemble mon code ? A des instructions métiers écrites dans un très mauvais anglais :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$cashier&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// $caissier-&amp;gt;encaisse($panier)&lt;/span&gt;
&lt;span class="nv"&gt;$cashier&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$card&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// $caissier-&amp;gt;debite($carte)&lt;/span&gt;
&lt;span class="nv"&gt;$accountant&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// $comptable-&amp;gt;ecrit($paiement)&lt;/span&gt;
&lt;span class="nv"&gt;$compliance&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// $controlleurFinancier-&amp;gt;verifie($paiement)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quoi ressemble mes classes "domain" (métiers) ? Là encore, à des concepts métiers écrits dans un anglais particulier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Legal\Company\France&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * French unique registration number for a specific establishments of a company
 * issued by INSEE
 * SIRET: "Système d'Identification du Répertoire des ETablissements"
 * 
 * @see https://www.economie.gouv.fr/entreprises/numeros-siren-siret
 */&lt;/span&gt;
&lt;span class="kd"&gt;Class&lt;/span&gt; &lt;span class="nc"&gt;Siret&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;RegistrationNumber&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;Siren&lt;/span&gt; &lt;span class="nv"&gt;$siren&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;Nic&lt;/span&gt; &lt;span class="nv"&gt;$nic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;siren&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Siren&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;mb_substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Nic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;mb_substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Exception&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\DomainException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Invalid SIRET number'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="err"&gt;  &lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getNumber&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;siren&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getNumber&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nic&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getNumber&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Legal\Company\France&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * Unique registration number for a french company
 * Issued by INSEE, composing the first part of SIRET
 * Système d'Identification du Répertoire des ENtreprises
 *
 * @see https://bpifrance-creation.fr/encyclopedie/formalites-creation-dune-entreprise/formalites-generalites/numeros-didentification
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Siren&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/^[0-9]{9}$/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\DomainException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Invalid SIREN number'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;

         &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getNumber&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les devs français sont sûrement familiers avec les concepts métiers ci-dessus. En revanche, si vous ne connaissez pas le fonctionnement des immatriculations de société en France, en lisant simplement ce code vous devriez avoir une idée assez claire sur le sujet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Les vertus
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Le code est "&lt;a href="https://medium.com/@daniel.oliver.king/writing-expressive-code-b69ef7a5a2fa#:~:text=This%20is%2C%20I%20think%2C%20one,human%20who%20reads%20our%20code." rel="noopener noreferrer"&gt;expressif&lt;/a&gt;"&lt;/strong&gt; pas seulement "lisible". Il traduit mot pour mot les concepts et le contexte métier.&lt;/p&gt;

&lt;p&gt;Le code n'est pas seulement "expressif", il est également "&lt;a href="https://en.wikipedia.org/wiki/Immutable_object" rel="noopener noreferrer"&gt;immutable&lt;/a&gt;". Je ne me suis pas imposé un design pattern ou une "best practice". J'ai simplement respecté les règles des immatriculations d'entreprises françaises (SIRET, SIREN &amp;amp; NIC), ces numéros sont immuables donc mon code est immuable... et donc ces objets s'apparentent naturellement à des "Value Objects", chouette. Le fait qu'ils soient des VO n'est donc qu'une simple conséquence de l'implémentation des spécifications métiers!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://youtu.be/HW1XZcs0UU8" rel="noopener noreferrer"&gt;La cuillère n'existe pas&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Oubliez les "best practices", les "design patterns", les "fluent interface", "event dispatching", MVC, SoC, SRP, SOA, RAD, CRUD, DRY... &lt;strong&gt;Oubliez la tech, seul le métier pilote l'implémentation, le métier est l'unique priorité.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Utilisez votre langage (ici PHP) pour modéliser le métier le plus simplement possible. Tel l'expert qui explique son métier à un junior, le dev "explique" explicitement et clairement dans son code un process métier.&lt;/p&gt;

&lt;p&gt;Si vous implémentez des Entités avec des setters &amp;amp; getters, des "Value Object" dans le namespace &lt;code&gt;\ValueObject\&lt;/code&gt;, vous êtes en train de manquer l'essentiel du DDD.&lt;/p&gt;

&lt;p&gt;J'insiste: le DDD ne s'intéresse pas à l'implémentation, DDD c'est le partage d'un langage commun entre le métier et le code.&lt;/p&gt;

&lt;p&gt;Dernière remarque au sujet de la valeur du DDD:&lt;br&gt;
Toutes les bases de code ont de la valeur au moment de l'exécution par la machine. &lt;strong&gt;Concernant les bases de code DDD, elles ont une très forte valeur même non-exécutées. Ce sont des centres de connaissance, des patrimoines métiers pour les personnes qui travaillent avec.&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  DDD et Symfony
&lt;/h1&gt;

&lt;p&gt;J'ai découvert le DDD autour de 2014, il m'a fallu une année complète, intensive de lecture et d'implémentation pour me sentir à l'aise et "efficient" avec mes outils (PHP, Symfony &amp;amp; Doctrine). Car comme &lt;a href="https://twitter.com/ericevans0" rel="noopener noreferrer"&gt;Evans&lt;/a&gt; le mentionne:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Don't fight your framework".&lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215" rel="noopener noreferrer"&gt;Domain-driven Design: Tackling Complexity in the Heart of Software&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En fonction des projets et de leur complexité, j'adapte mes implémentations. Mais généralement lorsque j'amorce un nouveau projet ou que je coach un nouveau dev sur le DDD, je suis ces grandes lignes directrices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;J'utilise &amp;amp; j'abuse du &lt;a href="https://olvlvl.com/2018-04-command-dispatcher-pattern" rel="noopener noreferrer"&gt;command pattern&lt;/a&gt; (&lt;a href="https://symfony.com/doc/current/components/messenger.html" rel="noopener noreferrer"&gt;Symfony Messenger&lt;/a&gt; est parfait)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Je n'utilise pas le pattern &lt;a href="https://martinfowler.com/bliki/CQRS.html" rel="noopener noreferrer"&gt;CQRS&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Je n'utilise pas le pattern &lt;a href="https://martinfowler.com/eaaDev/EventSourcing.html" rel="noopener noreferrer"&gt;ES&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;J'utilise &lt;a href="https://github.com/Behat/Behat" rel="noopener noreferrer"&gt;Behat&lt;/a&gt; &amp;amp; le &lt;a href="https://www.arolla.fr/blog/2012/06/bdd-c-est-quoi-donc/" rel="noopener noreferrer"&gt;BDD&lt;/a&gt; au niveau de mes commandes/handlers, couverture de test intégrale&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Je n'utilise que très rarement les tests unitaires, 20-30% de couverture en général.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Pourquoi ni CQRS ni d'ES ?
&lt;/h2&gt;

&lt;p&gt;Parce que ça coûte un rein! (voir &lt;a href="https://medium.com/tiller-systems/pourquoi-avoir-choisi-dutiliser-l-architecture-cqrs-e04c082f8b5f" rel="noopener noreferrer"&gt;l'article suivant qui présente la stack soft &amp;amp; hard d'un ES&lt;/a&gt;.)&lt;br&gt;
Ce sont des solutions ultra puissantes. Mais à déployer uniquement lorsque cela fait sens (rapport coût &amp;amp; besoin).&lt;/p&gt;
&lt;h3&gt;
  
  
  ES
&lt;/h3&gt;

&lt;p&gt;Je pense qu'il n'y a pas beaucoup de dev php qui sont familiers avec l'event sourcing, encore moins qui sont expérimentés pour créer, maintenir ou modéliser un event store correctement dans le temps. Souvent, au début d'un projet on manque de "maturité" modèle, on apprend, on refacto et on migre.  Mais sur un event store, aucune migration. L'historique reflète directement les erreurs d'implémentation passées, ce qui multiplie drastiquement les coût de maintenance logicielle.&lt;/p&gt;
&lt;h3&gt;
  
  
  CQRS
&lt;/h3&gt;

&lt;p&gt;Le CQRS c'est vraiment puissant quand on passe sur des problématiques/volumes de très grande échelle. Dans ce type de contexte, le CQRS apporte directement la complexité de la "&lt;a href="https://fr.wikipedia.org/wiki/Coh%C3%A9rence_(donn%C3%A9es)#Coh%C3%A9rence_%C3%A0_terme" rel="noopener noreferrer"&gt;Cohérence à terme&lt;/a&gt;" aka &lt;a href="https://stackoverflow.com/questions/10078540/eventual-consistency-in-plain-english" rel="noopener noreferrer"&gt;eventual consistency&lt;/a&gt;. Je vous laisse envisager le (sur)coût de cette spécificité.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;j'ai eu des expériences avec les 2 patterns, séparément et simultanément, dans des projets DDD, avec Symfony, parfois Doctrine et SQL/MongoDB&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  La structure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;src/Controller/&lt;/code&gt;: Symfony controller&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;src/Command/&lt;/code&gt;: Symfony CLI commands&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;src/Action/&lt;/code&gt;: Command + CommandHandler &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;src/Domain/&lt;/code&gt;: AR, Entity, VO, Repository Interface&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;src/*&lt;/code&gt;: Infrastructure, other stuff.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Loin d'être idéal, le répertoire nommé &lt;code&gt;/Domain/&lt;/code&gt; est plutôt pratique. On peut remplacer le mot "Domain" par le nom spécifique du métier qu'on est en train d'adresser, par exemple: &lt;code&gt;/Ecommerce/&lt;/code&gt;. &lt;/p&gt;
&lt;h3&gt;
  
  
  Le débat sur la pluralité des "domains"
&lt;/h3&gt;

&lt;p&gt;Les questions existentielles apparaissent dès qu'on manipule plusieurs domaines dans son projet. Il y a des écoles &amp;amp; courants de pensées sur la question de l'existence du &lt;code&gt;Core Domain&lt;/code&gt; et la séparation des &lt;code&gt;Supporting Domains&lt;/code&gt;. De mon côté, c'est tranché,  ces derniers sont organisés sans distinction particulière dans &lt;code&gt;/Domain/&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/Domain/Payment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/Domain/Compliance&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/Domain/Delivery&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Problème réglé, c'est cohérent (consistent), ça gagne à tous les coups.&lt;/p&gt;
&lt;h3&gt;
  
  
  Le contenu du "Domain"?
&lt;/h3&gt;

&lt;p&gt;A l'intérieur de &lt;code&gt;src/Domain/&lt;/code&gt;, j'ai l'ensemble des concepts métiers &lt;strong&gt;utiles&lt;/strong&gt; à l'implémentation. Il y a certaines classes qui sont persistantes (en base de données), d'autres non. On y trouve également les services métiers.&lt;/p&gt;

&lt;p&gt;Comme &lt;code&gt;src/Entity&lt;/code&gt; n'existe plus, la configuration de Doctrine est modifiée pour scanner le répertoire &lt;code&gt;src/Domain/&lt;/code&gt; dans son ensemble. Les annotations sont utilisées pour définir le mapping en base, car la &lt;a href="https://dev.toPrincipe%20de%20localit%C3%A9"&gt;localité&lt;/a&gt;, c'est également valable pour mon cerveau.&lt;/p&gt;

&lt;p&gt;Evidemment, lorsque le projet s'alourdi, il ne faut pas hésiter à &lt;strong&gt;ré-organiser &lt;code&gt;/Domain/&lt;/code&gt; en reflétant les besoins métiers&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/Domain/Legal/Company/France/RegistrationNumber/Siret.php
/Domain/Legal/Company/France/RegistrationNumber/Siren.php
/Domain/Legal/Company/France/RegistrationNumber/Nic.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a href="https://youtu.be/3HUWUtTZvK4?t=10" rel="noopener noreferrer"&gt;Commande qui conquiert&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  L'usage
&lt;/h3&gt;

&lt;p&gt;J'utilise systématiquement le command pattern pour les raisons suivantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;les &lt;em&gt;command handler&lt;/em&gt; s'inscrivent parfaitement dans les architectures hexagonales&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;les &lt;em&gt;command handler&lt;/em&gt; offrent de la cohérence sur la manipulation de ma couche "modèle“, ils en sont les uniques points d'entrée.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;les &lt;em&gt;command handlers&lt;/em&gt; clarifient les frontières transactionnelles d'une opération métier. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;les &lt;em&gt;command handlers&lt;/em&gt; normalisent la couche de service applicative&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;les &lt;em&gt;command handler&lt;/em&gt; soulagent les prises de tête sur les Aggregate Root&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;J'abuse pas mal de ces &lt;em&gt;command handlers&lt;/em&gt; car je les détourne parfois de leur but premier:&lt;/p&gt;

&lt;h3&gt;
  
  
  L'abus
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Lorsque j'ai beaucoup d'inconnus, d'incertitudes, un manque de compréhension, ou simplement lorsque je suis fainéant ou pressé: je les utilise pour stocker du code métier temporairement, à vocation à être déplacé ultérieurement dans des services métiers ou des objets métiers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lorsque je dois synchroniser plusieurs process métier entre eux: je les utilise au lieu d'implémenter des films d'horreur nommés &lt;a href="https://blog.couchbase.com/saga-pattern-implement-business-transactions-using-microservices-part/#:~:text=The%20SAGA%20Pattern,data%20within%20a%20single%20service." rel="noopener noreferrer"&gt;SAGA&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lorsque l'implémentation d'un AR est particulièrement subtile, difficile ou coûteuse avec Symfony/Doctrine, je code une partie de cette logique dans un handler (ça craint, vraiment à éviter).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Cela doit sonner très faux aux oreilles des puristes, mea maxima culpa. J'ai fait mon deuil &amp;amp; je suis complètement dans l'acceptation car ça fonctionne très bien pour moi.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Le naming
&lt;/h3&gt;

&lt;p&gt;Les commandes sont nommées d'après les processus métiers, les actions métiers qu'elles représentent. L'organisation s'adapte évidemment selon les besoins spécifiques:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/Action/Checkout.php
src/Action/CheckoutHandler.php

src/Action/FraudulentPaymentDeclaration.php
src/Action/FraudulentPaymentDeclarationHandler.php

src/Action/Compliance/Registration.php
src/Action/Compliance/RegistrationHandler.php
src/Action/Compliance/FrenchEstablishmentRegistration.php
src/Action/Compliance/FrenchEstablishmentRegistrationHandler.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Maybe I would have used an imperative form for the Command: Register instead of Registration&lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://twitter.com/docteur_klein/status/1267008375646302214?s=20" rel="noopener noreferrer"&gt;Florian Klein&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Un process métier, une action métier porte généralement un nom. Il est important de prendre le temps d'identifier clairement une action métier par son nom. Ce n'est pas toujours possible.&lt;/p&gt;

&lt;p&gt;Un verbe est un indicateur d'une potentielle faiblesse de compréhension du métier, exemple: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MarkAsRead&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ValidateStatus&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Attention, c'est une direction, dans mes dépôts il arrive très souvent d'avoir des commandes débutant par un verbe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Détail d'implémentation des commandes
&lt;/h3&gt;

&lt;p&gt;Les commandes sont donc les expressions d'une intention (métier). Elles sont modélisées &amp;amp; implémentées comme de simple &lt;a href="https://en.wikipedia.org/wiki/Data_transfer_object" rel="noopener noreferrer"&gt;DTO&lt;/a&gt; qui n'acceptent que des &lt;a href="https://en.wikipedia.org/wiki/Language_primitive#High_level_language_primitives" rel="noopener noreferrer"&gt;primitives&lt;/a&gt; facilitant &lt;a href="https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)" rel="noopener noreferrer"&gt;l'architecture hexagonale&lt;/a&gt; ainsi que l'application &lt;a href="https://matthiasnoback.nl/2017/08/layers-ports-and-adapters-part-3-ports-and-adapters/" rel="noopener noreferrer"&gt;port/adapter&lt;/a&gt;. Les commandes représentent la frontière entre les primitives et les concepts métiers dans mon application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Action\Compliance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Domain\Geo\Address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Domain\Geo\Country&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Registration&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressLine1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressLine2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressZip&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressLocality&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressCountry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressLine1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressLine2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressZip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressLocality&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$addressCountry&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;
    &lt;span class="c1"&gt;// ... boring stuff&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Uuid&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uuid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getAddress&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Address&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addressLine1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addressLine2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addressZip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addressLocality&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addressCountry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;J'ajoute généralement de la validation Symfony sur les commandes pour gérer les erreurs "de forme" et avertir l'utilisateur. Là encore, via des annotations. En revanche, les règles métiers fortes sont toujours exprimées &amp;amp; implémentées dans les objets métiers (domain), une violation de ces règles entraîne systématiquement une levée d'exception.&lt;/p&gt;

&lt;p&gt;Avec cette approche, l'implémentation de l'architecture hexagonale est enfantine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Les &lt;a href="https://symfony.com/doc/current/form/use_empty_data.html" rel="noopener noreferrer"&gt;Symfony/Form peuvent déclarer le DTO commande comme model&lt;/a&gt;, la fameuse data-class&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Les commandes (CLI) de Symfony/Console peuvent initialiser le DTO commande avec ces arguments&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Un Worker/Consumer RabbitMQ peut désérialiser un message et le transformer en DTO commande&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Un Symfony/EventListener peut transformer un Event en DTO commande&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Web Socket, etc...&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En revanche, une fois que ma commande est transmise à son &lt;em&gt;Handler&lt;/em&gt;, ce dernier manipule essentiellement des objets métiers (VO, Entity). Un objet métier fournit des garanties fortes: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Un état systématiquement valide (d'après les règles métiers)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Un état quasi-systématiquement immuable (immutable). &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Une API publique autorisant uniquement des changements d'état prévus &amp;amp; spécifiés par le métier&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Manipuler ce type d'objet métier est un réel bonheur, un soulagement: la &lt;a href="https://www.lilobase.me/la-complexite-une-histoire-de-charge-cognitive/" rel="noopener noreferrer"&gt;charge cognitive&lt;/a&gt; est très faible, inversement à la confiance dans le système, très forte. &lt;/p&gt;

&lt;p&gt;Quelques remarques :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Pas de typage spécifique pour les ID des AR et des entités (UserId, BookId, ...). Pénible à maintenir avec Doctrine. Dans certains projets très complexes, il m'est arrivé d'en utiliser.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Comment arbitrer entre VO (objet métier) et primitive?&lt;br&gt;
Si le métier porte de l'importance à un concept, si le métier à des règles spécifiques liées à un concept : VO. Sinon, les primitives sont suffisantes. Cela reste du PHP, pas du Java.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Les &lt;em&gt;command handlers&lt;/em&gt; ?
&lt;/h3&gt;

&lt;p&gt;Le rôle des &lt;em&gt;command handlers&lt;/em&gt; est de coordonner AR, les Entités, en bref, de coordonner les concepts métiers entre eux.&lt;/p&gt;

&lt;p&gt;Très souvent ils utilisent un &lt;code&gt;repository&lt;/code&gt; pour hydrater l'AR concerné par l'action, la commande métier.&lt;/p&gt;

&lt;p&gt;Le &lt;em&gt;handler&lt;/em&gt; appelle ensuite des méthodes sur les objets métiers (AR/Entité). Doctrine implémentant le &lt;a href="https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/tutorials/getting-started.html#what-is-doctrine" rel="noopener noreferrer"&gt;data-mapping&lt;/a&gt;, le &lt;em&gt;handler&lt;/em&gt; gère également les cycles de la couche de persistance. En sus, certains &lt;em&gt;handlers&lt;/em&gt; coordonnent d'autres détails d'infrastructure. Très souvent relatifs aux IO nécessaires à la composition d'un état métier correct (satisfaisant) répondant au pré-requis d'une action métier souhaitée.&lt;/p&gt;

&lt;p&gt;Symfony/Messenger prend entièrement à sa charge la gestion des commandes et des handlers. Depuis un controlleur HTTP, une Symfony/Commande ou encore un Worker RabitMQ, il suffit d'injecter le bus et &lt;a href="https://symfony.com/doc/current/components/messenger.html#bus" rel="noopener noreferrer"&gt;de dispatch votre commande&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;En cas de process métiers (trop) longs, passer les traitements des commandes en asynchrones ne nécessite quasiment aucun effort d'implémentation. Une &lt;a href="https://symfony.com/doc/current/messenger.html#transports-async-queued-messages" rel="noopener noreferrer"&gt;simple modification de la configuration de Symfony/Messenger&lt;/a&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;la commande est envoyée via un RabbitMQ. &lt;/li&gt;
&lt;li&gt;Le handler connecté en tant que "consumer" de la queue. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comme nous n'utilisons pas CQRS, une forte majorité des exécutions de commandes est synchrone, il suffit d'utiliser les fonctionnalités de &lt;a href="https://symfony.com/doc/current/messenger/handler_results.html" rel="noopener noreferrer"&gt;Symfony/Messenger pour récupérer le résultat d'un handler&lt;/a&gt;, ajouter un peu de sérialisation pour obtenir une belle représentation &lt;a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm" rel="noopener noreferrer"&gt;REST&lt;/a&gt; pour une réponse HTTP.   &lt;/p&gt;

&lt;h2&gt;
  
  
  Les Bounded Contexts (BC)
&lt;/h2&gt;

&lt;p&gt;Par défaut, je considère qu'un dépôt git représente 1 BC. Ma compréhension évolue souvent lors d'un projet. Ou tout simplement, le projet grandi, grossi et se découvre également de nouveaux BC's.&lt;/p&gt;

&lt;p&gt;Il m'arrive très souvent de séparer des contextes a posteriori. La présence de plusieurs contextes peut signifier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;une incompréhension du métier &lt;/li&gt;
&lt;li&gt;de fortes incertitudes sur le périmètre métier&lt;/li&gt;
&lt;li&gt;une extrême complexité&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Organiser ses BC's
&lt;/h3&gt;

&lt;p&gt;Les micro-services, c'est comme le CQRS: ça coûte un rein, si c'est mal orchestré: ça en coute deux. Si votre équipe dispose d'une armée de devops, vous avez de la chance. Sinon, le principe de localité, ça marche aussi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/BC1/Controller/
src/BC1/Command/
src/BC1/Domain/

src/BC2/Controller/
src/BC2/Command/
src/BC2/Domain/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pourquoi? Séparer ou délimiter des BC's est une opération très définissante. Très risquée également. Synchroniser des BC's entre eux est relativement coûteux. &lt;/p&gt;

&lt;p&gt;De cette façon, vous pouvez faire évoluer vos BC's, itérer, comprendre, refactoriser et déplacer du code de &lt;em&gt;domain&lt;/em&gt; en &lt;em&gt;domain&lt;/em&gt;, de &lt;em&gt;BC&lt;/em&gt; en &lt;em&gt;BC&lt;/em&gt;. Lorsque vous atteignez la "maturité modèle" et maitrisez la compréhension métier. Vous avez toute la liberté de séparer vos BC sur plusieurs dépôts... et de jouer avec &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;K8s&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Il est temps de prendre du recul à nouveau après cette plongée parfois très détaillée des techniques adoptées pour DDD &amp;amp; Symfony. Rappelez-vous le but de cet article et son message: "&lt;strong&gt;Oubliez la tech &amp;amp; concentrez vous sur le métier&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;Ces détails techniques sont un condensé de connaissances, d'expériences, d'astuces ou de compromis rassemblés au fil des années pour gérer simplement une approche DDD en synergie avec l'écosystème PHP et Symfony. Mais encore une fois: c'est le moins important du DDD.&lt;/p&gt;

&lt;p&gt;Ces méthodes sont à disposition dans &lt;strong&gt;l'espoir de ne plus s'attarder sur le "comment" du code. Mais de concentrer les efforts &amp;amp; la valeur sur le "quoi" et le "pourquoi".&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT JUNE 2nd&lt;/strong&gt; &lt;br&gt;
Lucas Courot a réagi &lt;a href="https://twitter.com/lucas_courot/status/1267596394107482113?s=20" rel="noopener noreferrer"&gt;via twitter&lt;/a&gt; sur 3 points: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;les commandes asynchrones&lt;/li&gt;
&lt;li&gt;le coût de gherkin (Behat)&lt;/li&gt;
&lt;li&gt;les notifications en place des exceptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Un échange de plusieurs tweets résume nos idées, opinions, divergentes ou convergentes, toujours intéressantes.&lt;/p&gt;

</description>
      <category>ddd</category>
      <category>php</category>
      <category>symfony</category>
      <category>programming</category>
    </item>
    <item>
      <title>Big ball of mud</title>
      <dc:creator>Ludovic Fleury</dc:creator>
      <pubDate>Sat, 31 Aug 2019 16:15:14 +0000</pubDate>
      <link>https://forem.com/ludofleury/big-ball-of-mud-1130</link>
      <guid>https://forem.com/ludofleury/big-ball-of-mud-1130</guid>
      <description>&lt;p&gt;Like many passionate people who program, I love progress. I'm fascinated by inventions and hyped by innovations.&lt;/p&gt;

&lt;p&gt;I truly believe that professionals in our sector are curious about technology with varying degrees. &lt;/p&gt;

&lt;p&gt;Many soft-skills or traits shape our work style. Rigorous, for instance, improves the robustness of our task. Creativity sparks crazy or smart hacks. Determination energizes our tough sprint.&lt;/p&gt;

&lt;p&gt;Curiosity powers us, combine it with passion and this premix could kick off any profile into any sector.&lt;/p&gt;

&lt;p&gt;Curiosity calls for discovery, innovation and progress. Obviously I never innovated, technically-speaking. Most of my “mind-blowing” or “life-changing” tech discoveries which pulsed my career were in fact established ideas that were theorised decades ago.&lt;/p&gt;

&lt;p&gt;Curiosity fertilizes technical debt, risk we couldn’t afford, knowledge we partially understood, patterns naively implemented.&lt;/p&gt;

&lt;p&gt;Bringing new tech, the last storage engine, the last framework and the last orchestrator is already a risk and you've got enough rants on Twitter for any of them. &lt;/p&gt;

&lt;p&gt;Worse… I’ve witnessed lately that devs would bring in architectures or software engineering principles hyped by the community. &lt;/p&gt;

&lt;p&gt;This. Is. A. Nightmare. Behind is a great intention, beyond is a technical debts factor on almost each commit. &lt;/p&gt;

&lt;p&gt;Few years ago, when a junior would build and run a Web based application: the code could be spaghetti and the dedicated Debian, a public trash. &lt;br&gt;
Now, imagine the same junior: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;a backend language/framework with CQRS/ES using NoSQL and SQL, probably some messaging bus.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a frontend framework. The whole frontend ecosystem is trying to reach maturity at the moment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a container orchestrator deployed in the cloud relying on 3 to 10 services.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;A decade ago, rookies were crafting artisanal mess. Now, they produce industrial disasters.&lt;/strong&gt; &lt;em&gt;I'm no better and would surely be a 10x on that field.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The problem is always the same: "meaning", the lack of. &lt;br&gt;
In our code, in our commit message, in our acronymized best-practices, in our company, in our team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internet is so real-time and crowded that trends are wrongly positioned as standards.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the last 5 years, php was stormed by DDD (domain driven design). Amazing. Now the reality will be more like a repository that’s totally messed up with the infra layer, app layer, core domain, aggregate root, services.&lt;/p&gt;

&lt;p&gt;DDD is trying to solve something so obvious that we missed the benefits of it, while paying the cost of the upfront complexity (+ the debt). &lt;/p&gt;

&lt;p&gt;This is one specific example but others crossed my paths regarding dev, ops, data…&lt;/p&gt;

&lt;p&gt;Stay focused on the meaning.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's the meaning of what I'm doing. &lt;/li&gt;
&lt;li&gt;What's the meaning of this feature at this stage of the project in this business context.&lt;/li&gt;
&lt;li&gt;What's the meaning of this pattern/tech.&lt;/li&gt;
&lt;li&gt;What's the meaning of these 400 LOC and 300K dependencies for this Hello World.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Iterate on your knowledge as well, failing is learning. Innovation is great: you bring it, you own the risk.&lt;/p&gt;

&lt;p&gt;A big ball of mud or a serverless microservice won't define a meaning or express any.&lt;/p&gt;

&lt;p&gt;The machine is still stupid, so our job is mostly to give it a meaning, &lt;strong&gt;meaningfully&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Azurrex for the proofreading&lt;/em&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>webdev</category>
      <category>programming</category>
      <category>career</category>
    </item>
  </channel>
</rss>
