Enzo Ruffin

PortFolio 2026

How I built a mobile ERP on my own

Why I started Olya, a mobile ERP built solo with React Native, Expo and a MERN architecture: the problems it targets and the technical choices behind it.

12 min read
Enzo Ruffin
Portfolio

Olya is a mobile ERP I build on my own: the product, the app, the API, the database, the trade-offs. The project did not start from the urge to try a technology. It started from something fairly ordinary I kept seeing at the companies I work with. Management tools are not missing. What is missing is any consistency between them. Here is why I started Olya, what it tries to solve, and how it is built.

Why a mobile ERP

In a small company, stock lives in a spreadsheet, customers in a free CRM, the schedule in a shared calendar, quotes in a Drive folder and decisions in a WhatsApp thread. Each tool does its own job perfectly well. The trouble shows up between them. The same information gets entered twice, edited on one side, forgotten on the other, and nobody can say which version is the right one. Teams spend part of their day retyping things, the owner has no overall view, and the question that comes up most often in meetings becomes 'where do we keep that again?'. That is where Olya starts. Not from a lack of features, from a lack of continuity.

What Olya does today

The app gathers the functions a company uses every day. Inventory tracks equipment and consumables. Planning organizes days, tasks and events. Team management holds the members of the company and their roles. The CRM centralizes customers and suppliers. The invoicing side prepares the commercial work. Notes keep in one place what usually ends up in a notebook or a message thread. And the company profile holds the details of the organization and its users. Taken one by one, these modules already exist elsewhere. What changes here is that they share the same database, the same account and the same permissions.

Why mobile first

Most management actions do not happen sitting at a desk. Checking there are still parts left before heading to a site, finding a customer's number from a van, pushing back a job because a meeting ran long, writing down a remark right after a visit. Traditional ERPs are designed for a large screen and a mouse, so those actions wait for the trip back to the office, when they are not lost along the way. Olya is designed the other way around. The home screen shows what concerns the current day, and a useful action has to fit in two or three taps. That constraint has a direct effect on the product: every screen forces a decision between what genuinely belongs on a phone and what can wait for a web version.

React Native and Expo

The app is written in React Native with Expo. React Native lets me keep my React habits while producing a real mobile application, not a website wrapped in a shell. Expo removes a good share of the friction: development environment, access to native features, builds, updates. Working alone on a project, that is time not spent in Xcode and Gradle configuration, so time that goes into features instead. The trade-off is real and worth knowing: as soon as a native piece falls outside the supported set, you move to a development build and get your hands back into the configuration. On this project the deal is still very much worth it.

Node, Express and a single source of truth

The server runs on Node with Express. It exposes the API the app consumes, and it holds the business rules. That matters even more in an ERP than elsewhere. A stock quantity, an access right, an invoice status: these are rules that must produce the same result no matter where the user acts from. If the mobile app decides on its own that a stock movement is valid, the rule now exists in two copies, and the day I add a web back office it will exist in three. So the app decides nothing. It displays, it sends, it reacts to the answer.

MongoDB and the domain models

The data sits in MongoDB, with models that follow the domain: Company, User, Inventory, Invitations, CrmClient. The document model suits the nature of the project. Companies do not all work the same way, needs move, and a young module gains a field every couple of weeks. Being able to add a piece of information without a heavy migration changes the pace when you develop alone. The flip side calls for discipline: an explicit Mongoose schema for every model, indexes added as soon as a collection is meant to grow, and never a document left lying around without a clearly identified owner.

Multi-company, the real difficulty

This is the point that shapes everything else. Every document belongs to a company, and every query has to be filtered by that company, without exception. One oversight on a single route and a customer sees someone else's inventory. The rule I hold myself to is simple: the app never sends the company id, it is derived from the user's token on the server. Roles are checked in the same place, in the business layer, and not only in the screen that hides a button. Invitations follow the same logic. You join an organization through a single use, time limited link that creates the membership and the role in one operation, which avoids the half-attached accounts nobody knows what to do with six months later.

An architecture built to take on modules

The split is a classic one, and that is exactly what I wanted: mobile interface, API, business logic, database. Each layer knows only the next. Adding a module therefore means writing a model, a service holding its rules, a few routes and the matching screens, without touching the rest. That is what lets a solo project keep moving six months in. A clever but tangled codebase costs little at the start, then charges full price for every new feature, until the point where rewriting feels easier than reopening the folder.

Navigation once the screens pile up

An ERP means a lot of screens, and on mobile that is the first place where things can fall apart. Each module has its own navigation stack: you come back to a module exactly where you left it, and the back gesture always does the same thing. It sounds like a detail as long as you have four screens. It is what makes the difference between a working tool and a sequence of pages. On top of that I keep one personal rule: no important piece of information should sit more than two levels deep from the home screen.

The native work people underestimate

Generating a PDF then sharing it, handling dates and time zones without shifting a job by a full day, requesting the right permissions at the right moment, keeping decent compatibility with Android phones that are not new. None of this shows up in a screenshot, yet it decides whether the app is genuinely usable in the field. I spent more time on document generation and sharing than on some entire modules, and it was the right call: a quote you cannot send from the phone sends the user back to their computer, which is where we started.

The mistakes that improved the code

The most instructive problems came from state. Data still on screen while it was already stale in the database, refreshes that did not fire at the right moment, screens remounting for nothing. The fix was not a patch on one screen but a change of method. Server state moved into a query cache, with explicit keys and invalidation after every write. Purely local state stayed local. Since then that category of bug has not come back, which is the only real proof a problem was handled at the root rather than covered up.

Working solo: the constraint becomes a filter

On your own you carry the product, the app, the API, the database, the tests and the release. There is no team to absorb a bad decision. So before every feature, three questions. Does it genuinely serve someone? Does it have to exist now? Will the current architecture still support it in six months? Plenty of ideas do not pass that filter, and that is exactly the point. What it taught me holds for any project, team or not: five coherent modules beat twelve half-finished ones.

The value of an ERP lies in the links

An ERP that lines up independent modules is worth nothing. Those are separate apps filed in the same menu. It gets interesting when a job from the schedule consumes equipment from the inventory, is attached to a customer from the CRM, carried out by a team member, and can turn into an invoice line. At that point the information is no longer entered three times, and the owner can answer the only question that really matters: where does the business stand. That is the direction the product follows, module after module, and it is what separates an ERP from a tidy drawer.

What comes next

The current foundation opens more doors than it closes: automation of repetitive tasks, fuller commercial management, dashboards, integrations with the tools already in place, AI features for data entry and search, project management, and above all workflows between modules. The rule I set myself is to add power only as long as it adds no visible complexity. An ERP becomes unusable the day it takes two days of training to record a customer.

What this project taught me

Much more than code. Choosing between two features when there is only time for one. Writing a codebase you can reopen in six months without rereading all of it. Accepting that an imperfect version should meet real usage rather than be polished in a vacuum. And seeing up close the difference between software that does a lot of things and software in which things work together.

What I am aiming for with Olya

Olya starts from a simple idea: make running a company more centralized, more mobile and more accessible. The project moves forward module by module, with React Native and Expo on the app side, Node, Express and MongoDB on the server side, and an architecture that leaves room for what follows. It is not only an application I am building. It is a view of what an ERP should be when it is designed for the person using it on their feet, between two appointments, rather than for the one filling in a table at the back of an office.

12 min read
Enzo Ruffin