Skip to content
Sebastián Aparicio

Case study · Gran Mayorista

From custom software to multi-tenant SaaS in six months

Gran Mayorista started by solving one distributor’s problem. When other businesses in the same sector asked for it, the decision was not to sell copies of the system: it was to rebuild the foundation so a single deployment could serve many companies without their data ever touching.

Role
Architecture, development, deployment and support
Period
December 2025 – present
Status
In production, more than 10 businesses
Team
Me on engineering, 2 partners on commercial expansion

The problem

The first version was built for one wholesale distributor: their catalog, their prices, their employees, their payroll. It worked because every business assumption was baked into the code and the data schema.

Within months other wholesalers asked for the same thing. The short path — cloning the project and deploying one instance per customer — solves the first sale and destroys the tenth: every fix has to be applied ten times, every database drifts on its own, and support cost grows linearly with customers.

The real problem was not adding customers. It was no longer having one system per customer.

Before · one instance per customer

Today · one deployment, many companies

Every fix is applied N times, and every database drifts on its own.

With no company context the query fails: isolation does not depend on remembering it.

Comparison diagram. On the left, the previous model: each business had its own copy of the application and its own database, three independent deployments. On the right, the current model: all three businesses enter a single deployment that queries one database, where every row hangs off a company identifier.

The rewrite in one picture: from one copy of the system per customer, to a single deployment where the company identifier is mandatory on every query.

Architecture decisions

One deployment, many companies. Every domain entity now hangs off a company identifier, and data access was locked down so no query can run without that filter. Isolation does not depend on a developer remembering to apply it: if the company context is missing, the query fails.

Roles and permissions scoped per company, not globally. A user exists inside a company and their permissions resolve within that scope, because the owner, the salesperson and the warehouse clerk see different parts of the same system.

DIAN electronic invoicing was built once, outside the product. Instead of embedding the regulation into each system, I built a shared service on top of the Factus API that Gran Mayorista, AutomatIQ POS and Mechss all consume. Factus lets a parent company buy a package of electronic documents and distribute it across the businesses using its software, so no small business has to manage its own provider.

The DIAN module is a separate piece from the main system. That was an explicit user request, and it lines up with the technical decision: it keeps regulatory compliance decoupled from the operational core.

Outcome

More than 10 wholesale businesses run on a single deployment today. A fix ships once and reaches all of them.

Onboarding a new customer stopped being an infrastructure project and became a record in the system.

Commercial expansion is run by my two partners; I keep the platform standing. That split is why growth is not capped by my available hours.

What is next

Finish DIAN electronic invoicing via Factus and roll it out across all three products.

The product vision is a network across the associated wholesalers, so businesses already sharing the platform can trade with each other.

Want to talk about this work or about a role?

sebastianmunoz603@gmail.com