Why Most Agencies Build Monoliths (Even When They Say Architecture)

    The agency said “clean architecture.” The codebase said otherwise. Sixteen custom modules, three overridden core controllers, a service layer that talks directly to the database, and a Cron job that synchronizes ERP data by calling a third-party API from inside a Magento observer. Architecture? Sure — just not the kind anyone intended.

    This isn’t a story about incompetent developers. It’s a story about incentives.

    The Incentive Problem Is the Architecture Problem

    Before blaming the code, look at the contract.

    Most agency projects are fixed-scope, fixed-price. The agency wins the deal by promising a specific set of features at a specific cost. Every hour spent designing integration layers, defining service boundaries, or building proper middleware is an hour that doesn’t appear in the deliverable list. It comes out of margin.

    So what happens? Everything goes into Magento. Observers become integration endpoints. Plugins become business logic containers. The codebase becomes a single organism where changing one thing requires understanding all of it.

    The agency doesn’t set out to build a monolith. They set out to ship a project within a budget. The monolith is a side effect of how the budget works.

    This is the first uncomfortable truth about agency architecture: the technical decisions are financial decisions in disguise.

    What “Everything in Magento” Actually Costs

    When all business logic lives inside Magento, you’ve made a specific trade:

    • Short-term: faster to deliver, easier to demo, less infrastructure to explain to the client
    • Long-term: Magento upgrade = business logic upgrade. Every patch version is a risk assessment.

    The concrete costs appear 18–24 months post-launch:

    • A Magento security patch breaks a custom observer that was intercepting core payment flow
    • An ERP vendor changes their API; the integration point is buried in a plugin that three other plugins depend on
    • The client wants to add a new storefront; the entire backend logic is Magento-specific and can’t be reused

    The irony is that clients often don’t see these costs as architecture failures. They see them as normal maintenance. And agencies are often still on the hook for that maintenance — reinforcing the pattern.

    The Integration Expertise Gap

    Part of the problem is skill set. Most Magento developers are exceptional at Magento. They know DI, service contracts, plugins, observers, cron, GraphQL resolvers. What they’re often less comfortable with is building outside Magento: standalone PHP services, Go microservices, message queues, webhook processing infrastructure.

    So when a project requires synchronizing with an ERP, the path of least resistance is:

    // In a Cron job inside Magento
    class SyncOrders
    {
        public function execute(): void
        {
            $orders = $this->orderRepository->getList($criteria);
            foreach ($orders as $order) {
                $this->erpClient->pushOrder($order);
            }
        }
    }

    This works. It ships. It even passes code review. But it’s a wrong boundary decision — Magento now owns the ERP sync logic, the ERP client configuration, the retry behavior, and the failure surface.

    The alternative — a dedicated sync service that reads Magento orders via API and pushes to ERP independently — requires comfort with building services outside the Magento container. That’s a different skill, and it’s not the skill most Magento teams are hired for.

    Patterns That Create Accidental Monoliths

    These are the patterns I keep seeing in audits:

    Observer chains as integration pipelines. An observer fires on checkout_submit_all_after. It calls a loyalty API. Another observer on the same event calls a CRM. A third calls a tax service. Each works fine independently. Together they create a request pipeline that blocks checkout if any external service is slow.

    Plugins that own domain logic. A plugin on OrderRepository::save() validates business rules that should live in the order creation flow. Now OrderRepository is load-bearing for business logic it was never designed to carry.

    Custom modules as mini-apps. A single module handles product import, price sync, inventory update, and fulfillment routing. It has its own database tables, its own cron jobs, its own configuration section. It’s not a Magento module — it’s an application that happens to live inside Magento.

    API integrations via cron. Instead of event-driven webhooks or message queues, data moves between systems on 15-minute cron cycles. This isn’t an architecture decision; it’s the default when no one designs the integration layer.

    The Boundary Model Agencies Avoid

    A clear system boundary would look like this:

    • Magento handles: catalog, pricing, checkout, customer accounts, order state machine
    • Integration layer handles: ERP sync, external API calls, webhook processing, data transformation
    • External systems handle: fulfillment, loyalty, CRM, tax calculation

    The integration layer can be as simple as a dedicated PHP service or as sophisticated as a Go microservice with a message queue. The point is that it exists as a separate deployment unit with its own failure domain.

    This boundary matters because:

    • Magento upgrades don’t break ERP integration logic
    • External API failures don’t crash checkout
    • The integration layer can scale independently
    • Different teams can own different boundaries

    Agencies avoid this pattern because it adds infrastructure. Another server, another deployment pipeline, another thing to explain to the client. In a fixed-price contract, that infrastructure has no line item.

    Decision Framework

    Build inside Magento when: – The logic is genuinely part of the commerce flow (pricing, cart rules, checkout steps) – It uses Magento data that isn’t available via API without significant overhead – The logic changes with Magento version and should stay coupled

    Build outside Magento when: – You’re calling external APIs that could be slow or unavailable – The logic involves data transformation or routing between systems – The functionality needs to scale independently of Magento – Multiple systems (not just Magento) will need the same capability – You’re building anything that resembles a pipeline or workflow engine

    Red flags that signal a boundary violation: – An observer that makes external API calls – A cron job that queries Magento AND an external system – A plugin that owns retry logic for external service calls – A custom module with its own database tables for non-commerce data

    The Leadership Question

    As a tech lead, the question is not “Can we build this inside Magento?” — the answer is almost always yes. The question is “What is the upgrade cost if we do?” Every piece of business logic inside Magento is logic you’ll need to verify, test, and potentially rewrite with every major version. A Magento 2.4.4 to 2.4.7 upgrade should not require rearchitecting your ERP integration.

    When you accept a project with a fixed budget and no line item for an integration layer, you’re making an implicit decision that your client will pay the technical debt later. Sometimes that’s the right trade. But it should be a conscious decision, not the default.

    Push back on scope. Document the boundary decisions. If the integration layer doesn’t fit in the budget, tell the client what they’re trading. That conversation is harder. It’s also the only way to stop building the same monolith on every project.

    Conclusion

    Most agencies don’t build monoliths because they don’t know better. They build monoliths because fixed-price contracts reward shipping over designing. The incentive structure produces the architecture.

    If you want to build differently, you have to change what you optimize for. Charge for architecture decisions. Build integration boundaries as first-class deliverables. Make the case that an integration layer is cheaper than a Magento upgrade gone wrong.

    The agencies that figure this out stop losing clients after year two. They become the people clients call to fix what the previous agency built. That’s not an accident — it’s what happens when you treat architecture as a service, not a side effect.

    Like What You Read?

    Let's discuss how we can help your e-commerce business

    Get in Touch →

    Stay Updated

    Get expert e-commerce insights delivered to your inbox

    No spam. Unsubscribe anytime. Privacy Policy

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    Let's Talk!