Freelance software engineering
I design, build and run a production SaaS on my own
Flutter and Firebase, from the database schema to the Play Store listing. Everything below is one shipped product you can open right now, not a portfolio of prototypes.
Alessandro Afonso FariaSoftware engineer · Flutter and Firebase · Brazil (UTC-3) · Remote
Marksym is an appointment and business management platform for service businesses: booking, point of sale, cash control, inventory, commissions, WhatsApp reminders and a public marketplace. I wrote the mobile apps, the web apps, the backend, the security model, the payment integration, the transactional email, the marketing site and the release pipeline.
The product
The problem
Salons, barbershops and clinics run their calendar on a phone. Bookings arrive by message at midnight, get written on paper, and the owner finds out about a double booking when both clients are standing at the door. Money is counted at the end of the day without knowing which professional earned what.
What I built
A multi-tenant platform with three front ends on one codebase: a management app for the business, a booking app for its clients, and a public marketplace where a customer finds a professional and books without installing anything. Each business gets isolated data, with its own staff, services, packages, inventory and cash register.
What it does
Calendar with per-professional availability, holidays and time off. Point of sale with open tabs, products and packages. Cash opening and closing, accounts payable, supplier records, stock movement, commission per professional and per service, and reporting on top of all of it. Automated appointment reminders on WhatsApp, prepaid by the business through instant bank transfer.
Where it runs
Live in production on the web for both audiences, and published as two separate Android apps built from the same repository through build flavors. The marketing site you are reading is generated by the same pipeline.
The codebase, measured
Counted directly from the repository every time this page is built, so the figures never drift from what is actually shipped.
- 181,000+lines of Dartacross 452 files in the app layer
- 21,000+lines of TypeScriptCloud Functions backend
- 1,800+automated testsunit and widget, in 148 files
- 46backend endpoints38 callable, 3 webhook, 5 scheduled
- 2,646translation keysmaintained in three languages
- 550+commitssince April 2026, written solo
Architecture decisions worth defending
A short list of choices I would be glad to be questioned about, and the reason behind each one.
Multi-tenant by document path, not by column
Every business owns a subtree of the database, and the security rules resolve the caller role against the organisation in the path itself. Data from two businesses cannot meet by accident, because no query spans them without an explicit collection group index that I control.
Billing fields the client cannot write
Plan, subscription status and credit balance are rejected by the security rules on any write coming from a browser. They change only inside a Cloud Function, after a payment is verified. The first version of that feature trusted the client, and closing the hole is the change I am most glad I made.
Scheduled work split into dispatcher and worker
The reminder job does not send messages. It scans a single time window, publishes one message per appointment to a queue, and returns. A separate worker sends and marks the appointment. A slow provider then delays one message instead of timing out the whole job, and a retry cannot send the same reminder twice.
A payment webhook that assumes it can be forged
The instant-payment provider does not offer mutual TLS on the plan in use. Rather than trusting the callback, the endpoint treats it as an untrusted ping: it re-reads the charge from the provider API, checks the amount and the identifier it issued, and credits the account once, idempotently.
The marketing site is static HTML, not the app
A Flutter web app paints to a canvas and leaves a search engine with an empty page. The public site is pre-rendered HTML generated from the same translation files the app uses, served at the root, with the application moved under its own path. Crawlers read real text and the app keeps its single codebase.
One source of truth for price
Prices are edited in an admin panel and stored once in the database. Nothing in the code, the translation files or the static HTML is allowed to contain a price literal. Pages are pre-rendered with the live value and corrected at runtime, so a price change needs no deploy and can never disagree with itself.
How I work
Errors travel as types, not strings
Services throw typed exceptions. Providers catch them and expose state. The UI renders that state. No service in the codebase imports a widget, opens a dialog or knows what a snackbar is, which is why the business rules are testable without a screen.
Tests ship with the change, not after it
Touching a class means updating its tests in the same commit, and the suite runs before anything is called done. Firebase is mocked everywhere, so tests never reach a real project.
Decisions are written down next to the code
Non-obvious choices carry a comment explaining what was tried first and why it failed. The comments in this repository are about reasons, not about what the line already says.
Three languages from day one
No user-facing string is hardcoded. Every label exists in Portuguese, English and Spanish, and the same files feed the apps, the marketplace and this website.
Continuous integration where failures are silent
The backend is type-checked and tested on every push. Deploys stay deliberate and per target, because a hosting target that auto-deploys is a hosting target that goes out broken on a Friday.
Stack
Client
- Flutter
- Dart
- Provider
- GoRouter
- Flutter Web
- Android
Backend
- Cloud Functions
- TypeScript
- Node.js
- Pub/Sub
- Cloud Scheduler
Data
- Cloud Firestore
- Security Rules
- Composite indexes
- Cloud Storage
Identity
- Firebase Auth
- Google Sign-In
- Role-based access
- Email verification
Integrations
- WhatsApp Cloud API
- Instant payments (Pix)
- Transactional email
- Google Places API
Web and delivery
- Static site generation
- Structured data
- Firebase Hosting
- GitHub Actions
- Play Console
What I can take on
Cross-platform apps
One Flutter codebase delivered to web, Android and iOS, including store submission, signing and release flavors.
Firebase backends
Firestore data modelling, security rules, Cloud Functions, scheduled jobs, queues and third-party webhooks.
Rescue and hardening
Taking over an existing Firebase project and closing the gaps that tend to hide in it: client-writable billing data, rules that allow cross-tenant reads, jobs that retry into duplicates.
Multi-tenant SaaS features
Subscription plans, role-based permissions, per-tenant configuration, usage quotas and admin tooling.
Get in touch
Available for contract and freelance work, remote. I am based in Brazil (UTC-3), which overlaps with United States business hours for most of the working day.
Send an email