When I started thinking seriously about AI-powered commerce applications, I quickly realized that:
- Technology stacks vary significantly across domains: AI ecosystems are largely Python-centric, while e-commerce platforms are commonly built on TypeScript or PHP.
- A highly decoupled architecture is essential to absorb the continuous evolution of AI technologies without disrupting the broader system.
- Adopting a microservices architecture enables scalability, flexibility, and compatibility with a diverse range of e-commerce platforms.
- The solution should support autonomous teams with different skill sets, allowing them to collaborate effectively while preserving clear ownership and reducing operational friction.
I do not want to integrate AI capabilities into the e-commerce solution which would turn the entire codebase into a tangled mess of prompts, agents, API calls, and business logic. I need to keep the commerce side stable while allowing the AI side to evolve at the speed of the AI ecosystem.
This architecture is the result of those questions and requirements. I decided to base it on Shopify as it is my current e-commerce plaform of choice and its Custom App system perfectly fits into the schema.
A typical request flow
A typical interaction looks something like this:
- A merchant triggers an action from the Shopify admin.
- The backend (a Shopify Custom App) receives the request.
- The backend calls the AI service.
- The AI service invokes agents and supporting services.
- The agents interact with one or more models.
- Results are returned to the backend.
- The backend persists data and updates Shopify if needed.
- The merchant receives the final result.
The same pattern works for storefront experiences. The only difference is where the request originates.
The principle: separate commerce from AI
The first decision I made was to separate the commerce platform from the AI platform. I wanted commerce-related concerns to live in one place and AI-related concerns to live somewhere else.
The architecture is therefore split into two major components:
- A Shopify application responsible for commerce workflows.
- An AI service responsible for orchestration, agents, and model interactions.
The two communicate through APIs, but remain largely independent. This separation has become one of the most important architectural decisions in the system.
Commerce and AI evolve at completely different speeds. Trying to make them coexist inside the same layer often creates unnecessary coupling.
The Shopify side
At the center of the commerce platform sits the Brush Backend, hosted on Gadget.dev. I decided to make this application the source of truth for everything related to the business domain.
Its responsibilities include:
- managing application data,
- exposing business APIs,
- integrating with Shopify,
- orchestrating commerce workflows,
- serving merchant and storefront experiences.
In other words, this layer understands products, customers, collections, orders, business rules and so on. It does not need to understand how an LLM works.
Leveraging Shopify’s APIs
The backend interacts with Shopify through its GraphQL APIs.
Admin API
The Admin API gives access to merchant-facing data and operational workflows. This is where the application can:
- read and update products,
- manage collections,
- access orders,
- retrieve customer information,
- synchronize business data.
Storefront API
The Storefront API exposes mainly product and shopi data that we can request as localized using the @inContext directive. Whenever I need data that belongs in the shopping experience, this is typically the interface I use. And, to make things easier, Brush comes with the ability to query the Storefront API out-of-the-box.
The merchant experience
For merchants admin experience, I stuck to the required Shopify application setup.
The administration interface is built with:
- React,
- Shopify Polaris,
- Shopify App Bridge.
This gives merchants an experience that feels native to Shopify and the application becomes part of their existing workflow.
The storefront experience
On the storefront side, the application integrates directly with the Shopify theme.
Communication happens through a Shopify App Proxy which, also, is ready to use thanks to the frontend stack of Brush.
I like this approach because it creates a clean separation between:
- the storefront,
- Shopify,
- the application backend.
From the shopper’s perspective, everything appears to happen inside the store. Behind the scenes, requests are routed to the application where the business logic lives.
This allows me to expose AI-powered capabilities on the storefront without exposing internal services directly.
Why I created a dedicated AI service
Again, instead of embedding prompts and agent logic directly into the e-commerce application, I built a dedicated service using FastAPI and deployed it independently and easily thanks to Cloudflare’s Workers.
At first, this may seem like an unnecessary layer but, to me, it allows matching the AI industry standards which are Python-based. So, every AI-related lies in a Python box.
Moreover, AI workloads have different characteristics:
- they evolve quickly,
- they require experimentation,
- they depend on external providers,
- they often need different observability tools,
- they benefit from independent deployment cycles.
So trying to force all of that into the commerce application felt wrong.
Inside the AI service
The AI service is organized into several layers.
REST API
The REST API powered by FastAPI acts as the entry point. Brush sends requests to the AI service without needing to know anything about the underlying implementation.
In other words: the commerce platform asks for a capability ; the AI platform decides how to execute it.
Services
Below the API layer sits a service layer.
This is where I centralize reusable AI capabilities such as:
- prompt management,
- retrieval pipelines,
- model routing,
- tool execution,
- orchestration logic.
This service layer is just enforcing software architecture best practices.
Agents
Above the services sits the agent layer where reasoning happens.
Agents can:
- coordinate multiple steps,
- invoke tools,
- retrieve information,
- make decisions,
- generate outputs.
Depending on the use case, an agent might:
- generate product descriptions,
- create SEO content,
- answer merchant questions,
- analyze catalog quality,
- recommend merchandising improvements.
Remaining model-agnostic
Another design choice I made early on was avoiding dependence on a single model provider. So the AI service can interact with either simple models, frontier models, local open-source ones, remote open-source, …
I did not want the application architecture to depend on the success or failure of a particular vendor. The AI landscape changes too quickly for that.
This also allows chosing the right model for the right task: not every task deserves a premium model. Some workflows only require:
- classification,
- extraction,
- summarization,
- lightweight generation.
Others genuinely need to benefit from stronger reasoning capabilities. Keeping providers abstracted allows the AI service to choose the right model for each taskm while the Shopify application doesn’t need to care.
Designing for change
One lesson I learnt in my experience in building solution and software architectures is that they rarely fail because they are too simple. They usually fail because they become difficult to change.
When I designed this system, I assumed three things:
- E-commerce platforms continue to evolve.
- AI models continue to evolve.
- Product requirements continue to evolve.
The architecture therefore optimizes for adaptability:
- The e-commerce application focuses on commerce.
- The AI service focuses on intelligence.
The two remain connected, but neither is tightly coupled to the implementation details of the other. At least for now, this feels like the cleanest way I’ve found to build AI-native commerce applications on top of an e-commerce platform (Shopify in the current case).