Re-architecting Salesforce: Modular, Scalable, and SOLID
Dikirimkan pada - Kali Terakhir Diubah Suai pada
<p id="ember892" class="ember-view reader-text-block__paragraph">In complex Salesforce implementations, it's easy to bundle everything into a single metadata layer. This not only slows down development but also increases the risk of bugs, code conflicts, and deployment failures.</p>
<p id="ember893" class="ember-view reader-text-block__paragraph">To address this, I recently came up with an initiative to redesign our Salesforce architecture around <strong>modular development</strong>, using <strong>unlocked packages</strong>, <strong>feature-based repositories</strong>, and aligning the internal code design with <strong>SOLID principles</strong>.</p>
<p id="ember894" class="ember-view reader-text-block__paragraph">The results were immediate: better scalability, cleaner code, and faster team collaboration.</p>
<h3 id="ember895" class="ember-view reader-text-block__heading-3">What is a Modular Structure in Salesforce?</h3>
<p class="ember-view reader-text-block__paragraph">A <strong>modular structure</strong> means organising your Salesforce codebase into <strong>independent, purpose-specific units</strong>. These units are implemented as <strong>unlocked packages</strong>—each one scoped to a specific domain or feature area like Opportunities, Cases, or Lead Conversion.</p>
<h3 id="ember897" class="ember-view reader-text-block__heading-3">Example package breakdown:</h3>
<p id="ember898" class="ember-view reader-text-block__paragraph"> </p>
<ul>
<li>Core – Common utilities, shared layouts, reusable interfaces, and custom fields</li>
<li>AccountManagement – Apex services, flows, Lightning Web Components (LWCs), and permission sets for Accounts</li>
<li>CaseHandling – Case-specific flows, automation, and data logic</li>
<li>LeadConversion – Lead-to-Opportunity conversion logic, subflows, and validation rules</li>
</ul>
<p>Each package lives in its <strong>own Git repository</strong> and is treated as an <strong>independent SFDX project</strong>. Teams can develop, test, and deploy these modules separately.</p>
<p id="ember900" class="ember-view reader-text-block__paragraph">Learn more: <a class="BJEbSaIJBPeWfhLqLplZbvbHLceVASpzKHENE " tabindex="0" href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_unlocked_pkg_whats_a_package.htm" target="_self" data-test-app-aware-link="">https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_unlocked_pkg_whats_a_package.htm</a></p>
<h3 id="ember901" class="ember-view reader-text-block__heading-3">Why Go Modular?</h3>
<p id="ember902" class="ember-view reader-text-block__paragraph">Implementing a modular structure brings immediate benefits:</p>
<ul>
<li><strong>Separation of concerns</strong> — Each package has a clear scope</li>
<li><strong>Independent pipelines</strong> — Smaller deployments and fewer conflicts</li>
<li><strong>Reusable components</strong> — Shared logic lives in Core, reused across modules</li>
<li><strong>Better team collaboration</strong> — Multiple teams can work in parallel</li>
<li><strong>Targeted testing</strong> — Easier to isolate and test business logic</li>
<li><strong>Improved CI/CD</strong> — Every module has its own validation and versioning process</li>
</ul>
<p> Recommended read: <a class="BJEbSaIJBPeWfhLqLplZbvbHLceVASpzKHENE " tabindex="0" href="https://developer.salesforce.com/blogs/2020/02/ci-cd-best-practices-for-unlocked-packages" target="_self" data-test-app-aware-link="">CI/CD Best Practices for Unlocked Packages</a></p>
<h3 id="ember905" class="ember-view reader-text-block__heading-3">Recommended Folder Structure (Per Module)</h3>
<p id="ember906" class="ember-view reader-text-block__paragraph">Each unlocked package follows the Salesforce DX source format. Here’s a simplified structure we use for each module:</p>
<pre class="language-markup"><code>classes/
domain/ - Business logic and service classes
triggers/ - Trigger handlers
interfaces/ - Interfaces and abstract base classes
util/ - Utility/helper classes
tests/ - Unit tests </code></pre>
<p id="ember907" class="ember-view reader-text-block__paragraph">All dependencies between packages (e.g., CaseHandling depends on Core) are managed via the sfdx-project.json file, making the system clean, explicit, and maintainable.</p>
<blockquote id="ember908" class="ember-view reader-text-block__blockquote">More on DX source structure: <a class="BJEbSaIJBPeWfhLqLplZbvbHLceVASpzKHENE " tabindex="0" href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_code_introduction.htm" target="_self" data-test-app-aware-link="">Salesforce DX Source Format Guide</a></blockquote>
<h3 id="ember909" class="ember-view reader-text-block__heading-3">CI/CD and Version Management</h3>
<p id="ember910" class="ember-view reader-text-block__paragraph">Each module has its own CI/CD pipeline that:</p>
<p id="ember911" class="ember-view reader-text-block__paragraph"> </p>
<ul>
<li>Spins up a scratch org</li>
<li>Installs dependent packages (e.g., Core)</li>
<li>Pushes only that module’s metadata</li>
<li>Runs tests</li>
<li>Creates and promotes package versions as needed</li>
</ul>
<p> </p>
<p id="ember912" class="ember-view reader-text-block__paragraph">This ensures changes are well-tested, isolated, and can be deployed independently across environments.</p>
<h3 id="ember913" class="ember-view reader-text-block__heading-3">Final Thoughts</h3>
<p id="ember914" class="ember-view reader-text-block__paragraph">If your Salesforce org is starting to feel tightly coupled or hard to manage, I highly recommend exploring a <strong>modular architecture with SOLID design principles</strong>.</p>
<p id="ember915" class="ember-view reader-text-block__paragraph">This structure allows you to:</p>
<p id="ember916" class="ember-view reader-text-block__paragraph"> </p>
<ul>
<li>Work at scale with multiple teams</li>
<li>Build and test in isolation</li>
<li>Release confidently with version control</li>
<li>Maintain a clean, extensible code base</li>
</ul>
<p> </p>
<pre class="reader-text-block__code-block"><code> </code></pre>
<p id="ember896" class="ember-view reader-text-block__paragraph"><br /><br /></p>