A business platform unifying loyalty programs, POS, and store management into a single ecosystem.
Every cafe in Turkey runs loyalty differently. Stamp cards that get lost in wallets, apps that nobody downloads twice, punch cards from 2003 — fragmented and forgettable. For the customer, it's ten cafes, ten systems, none of them talking to each other.
For the business owner, it's worse. They need a POS system, a loyalty program, staff management, inventory tracking — and they're duct-taping four different tools together. The barista at 8am with a line out the door shouldn't need to think about which app does what.
Lokal is one platform that does all of it. Three apps — one for the customer, one for the business, one for admin — sharing a common core. The customer sees one loyalty card per business. The business gets a full POS, menu management, staff roles, and real-time reporting.


Modular monolith .NET 10 backend, Flutter monorepo with 3 apps (lokal-common shared package), PostgreSQL database, and Redis cache. Real-time SignalR hub.

Generic RBAC doesn't distinguish between 'can manage this store's orders' and 'can manage all stores.' I encoded the scope directly into permission keys — Store.Order.Create vs Business.Order.Read. One lookup, scope inferred from the key.
The hard part wasn't building the menu — it was making it work when a business has 5 stores that each charge different prices for the same espresso, while still generating unified reports. Products are defined once at the business level, each store can override the price.
When a customer buys an espresso for 3.50, that price is frozen in the order forever — even if the menu changes tomorrow. Historical orders are immutable, reports show what customers actually paid.
A Barista sees different things than a General Manager. 7 default roles, custom role creation, and Business/Store scoped permissions ensure every employee accesses only what they need.
Products defined once, each store sets its own price. Store-level flexibility without breaking business-wide reporting.
Customer, business, and admin apps share a common core via the lokal-common package, while each maintains its own user experience.
The first auth system was too rigid — hardcoded roles, non-extensible permissions. The moment it blocked businesses from creating their own roles, I redesigned the entire system from scratch. Scope-encoded permission keys, dynamic roles, and representative overrides. This was the single biggest rewrite in the project and one of the most critical design decisions I've made.
Getting the store override layer right was harder than expected. Every query had to account for both business defaults and store overrides. In the first implementation, distinguishing between NULL overrides and 'no price' was a problem. Once I landed on the COALESCE pattern, everything clicked — but the path there involved several false starts.
Production Flutter apps (Customer, Business, Admin)
Faster query execution with Dapper (~45ms vs EF Core ~80ms)
Granular permission keys with 7 default roles
Deployed as FAB Coffee on App Store and Play Store
Let's talk about your project.
Get in touch