A few days ago, this project was still basically a login screen.
Now it has a responsive application shell, authenticated navigation, a module-based home screen, and its first real inventory flow connected to Supabase in production.
This article covers Sessions 5 and 6 of my project, ERP Modular, an open source ERP I am building as both a portfolio project and a deliberate way to learn professional software development in practice.
App demo video: [Watch the current app running here] https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z3cbtb0342nezld3g1bu.gif
https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cqjy3djighzy3vgzbk8r.gif
Why these two sessions mattered so much
Up to Session 4, I had already done the architectural planning, set the rules for the project, and implemented authentication. The system could log in and log out, but it still did not feel like an ERP yet.
Sessions 5 and 6 changed that.
Session 5 gave the project a real application structure:
- authenticated routing
- responsive navigation
- a reusable app shell
- a module-based home screen
- a standard error/result pattern
Session 6 delivered the first actual business module end to end:
- domain
- repository
- notifier
- presentation
- real data from Supabase
- validated Row Level Security
That combination was important because it was the first time the architecture stopped being an intention and started behaving like a system.
Session 5: from isolated login to real ERP navigation
Session 5 focused on the navigation infrastructure of the app. Instead of continuing to add isolated screens, I built the foundation that defines how a user moves through the ERP.
The key pieces implemented were:
-
GoRouterwithShellRoute - a centralized router file in
lib/core/router.dart -
RouterNotifierto react to authentication changes - a responsive
AppShell - a
HomeScreenwith module cards - placeholders for routes like dashboard and settings
- the
Resultado<T>pattern to standardize failures across layers
This was a big shift.
Before that, the project had a login screen and authentication flow. After Session 5, it had:
- automatic redirects based on authentication
- persistent navigation for authenticated screens
- different navigation behavior for desktop and mobile
- a home screen that already looked like the entry point of a modular ERP
According to my session notes, the project moved from “an isolated login screen” to “an application with real navigation — sidebar on desktop, mobile navigation, automatic authentication-based redirect, and a working home screen with module cards.” fileciteturn12file4
The responsive AppShell
One of the most important decisions from the previous architecture session was that the ERP should not let each screen invent its own layout. Instead, authenticated screens should live inside a shared shell. That decision became concrete in Session 5. The AppShell adapts navigation based on screen size, using a different structure for mobile, tablet, and desktop. fileciteturn12file4turn12file5
That matters more than it seems.
A login screen is just an entry point. A system starts to feel real when the user can move around it consistently.
Standardizing errors early
The other important addition in Session 5 was Resultado<T>, a sealed class used to standardize how repositories return either success or failure. From this point on, repositories are expected to return Resultado<T> instead of throwing unchecked exceptions directly. The context document explicitly defines this as one of the non-negotiable architectural rules of the project. fileciteturn12file0
That gave me a cleaner contract between layers:
- repository returns
Resultado<T> - notifier translates the result into state
- widget renders the state
It is a small pattern, but it removes a lot of ambiguity when the project starts growing.
A session with an unexpected security check
Session 5 also had something I did not expect: a security detour.
Because of a reported RAT issue tied to the JavaScript ecosystem, I paused and verified my machine and other projects for traces of the malicious package. The result was clean, but the process itself was valuable. It reinforced a habit I want to keep: when a security concern appears, verify first and keep going with evidence instead of panic. fileciteturn12file6turn12file13
Session 6: the first real module, end to end
If Session 5 made the project feel like an application, Session 6 made it feel like an ERP.
This was the first real implementation session of the inventory module. In one session, I built the product feature through all layers:
- domain
- infrastructure
- state management
- presentation
The concrete result was a working stock listing screen connected to Supabase in production, with:
- a rich
Produtodomain model - a repository contract in the domain
- a Supabase repository implementation
- a notifier with sealed states
- search by name and internal code
- soft delete with confirmation
- visual low-stock indicators
- a validated multi-tenant RLS behavior
The diary describes it as the first time a complete module was built “from domain to presentation in a single session,” with the stock module working through a full flow and RLS isolation validated in the database. fileciteturn12file1turn12file3
The Produto domain model
The Produto class became the richest domain entity in the project so far.
It includes identification, fiscal fields, stock quantities, pricing, location, image URL, reprocessing support, and multiple barcodes. It also includes derived properties like:
estoqueBaixoativo
That was an important design moment because I wanted the domain object itself to answer business questions.
For example, the UI should not decide whether stock is low. The product should know that.
This is one of the parts that made the architecture feel practical rather than theoretical.
The repository and the result pattern in practice
The IProdutoRepository contract was defined in the domain, and SupabaseProdutoRepository became the only class in the feature that actually knows Supabase.
That repository returns Resultado<T> for every operation, applies soft delete, and performs the data access logic while keeping the rest of the feature independent from the backend implementation. The session notes also mention that PostgreSQL error codes were classified in the repository, which is exactly the kind of boundary I wanted to establish between infrastructure and the rest of the app. fileciteturn12file1
Again, the rule stayed the same:
- widget does not know Supabase
- notifier does not know backend details
- repository knows infrastructure
- domain carries business meaning
A visual result that actually means something
By the end of Session 6, the stock screen was already showing three test products with meaningful feedback:
- a washer below minimum stock with a red indicator
- two other products above minimum stock with green indicators
- a visible product counter
- a working sidebar
- refresh button
- overflow menu
- a floating action button for future product creation
That is exactly the kind of progress I wanted from this project: not just “data on the screen,” but data rendered with domain meaning. fileciteturn12file1
The accidental RLS test
One of the best moments of Session 6 was not planned.
A test ended up confirming that Row Level Security was working correctly in production. The isolation by empresa_id was not just documented or assumed; it was observed behaving correctly. The context notes summarize it directly: RLS was validated in production, and data inserted with the wrong empresa_id stayed invisible to the user. fileciteturn12file0turn12file13
That was a very meaningful milestone for me.
Because at that point, the project was no longer just “my architecture idea.” It had already validated one of its most important real-world constraints.
What these sessions taught me
Sessions 5 and 6 reinforced something I am starting to understand more clearly:
architecture only starts to prove itself when real features begin to use it.
It is easy to feel good about clean folders and diagrams. It is harder, and much more valuable, to see those decisions survive:
- authentication redirects
- responsive navigation
- state transitions
- repository boundaries
- result handling
- database isolation
These two sessions were the first time I felt that happening consistently in the project.
What comes next
The next step is Session 7: parsing NF-e XML files and starting the fiscal side of the inventory workflow. The current context already defines the next targets:
XmlService- fiscal domain classes like
DocumentoFiscal,NotaFiscal, andItemNota - repository and notifier for invoice import
- a file selection screen
-
notas_fiscaisandnota_itenstables in Supabase. fileciteturn12file13turn12file17
This is important because inventory without invoice import would still be incomplete for the actual use case I have in mind.
Conclusion
Session 5 gave the ERP a real application structure.
Session 6 gave it its first real business module.
Together, they marked the moment when the project stopped feeling like a prototype and started feeling like the beginning of a real system.
That does not mean it is complete. It is not.
But it now has:
- authentication
- responsive navigation
- a modular home screen
- a working inventory listing
- search
- soft delete
- standardized error handling
- validated multi-tenant isolation
For me, that is a meaningful threshold.
If you are also building a portfolio project and trying to learn architecture through something real instead of toy examples, this has been one of the most rewarding parts of the journey so far.
I will keep documenting the next steps.
Top comments (1)
I’m documenting the full journey of building this ERP from scratch, step by step.
Project repo: github.com/Leoras-gg/erp-modular