Mobile apps break when the backend moves
The Flutter app I shipped had to stay aligned with REST APIs that were still evolving. Hand-written models rot fast. Widgets start calling HTTP directly. Then a single JSON field rename breaks half the screens.
I treated that as an architecture problem, not a widget problem. The app uses Domain-Driven Design so the domain stays readable, and Swagger / OpenAPI so models and API clients are generated instead of typed by hand.
The same idea shows up on the web side in React and Tailwind UIs. Desktop and mobile should both consume a contract, not invent one.
A DDD shape that fits Flutter
I do not copy a Java package tree into Dart. I keep three ideas:
- Domain: entities, value objects, and use cases with no Flutter imports.
- Data: generated OpenAPI clients, mapping, and repository adapters.
- Presentation: widgets, routing, and state that call use cases.
Widgets never see a raw Response. Use cases return domain types. If OpenAPI regenerates a DTO, only the data layer changes.
Generated clients from Swagger
The backend publishes OpenAPI. From that file I generate:
- Request and response models
- API client methods
- Basic validation for required fields
That is how the mobile app stayed consistent with REST without a weekly DTO cleanup. When a new delivery status appears in the API, regeneration plus a mapping test is enough.
UI/UX still matters
Architecture is useless if the screens feel cheap. I designed the interfaces around modern UX: clear hierarchy, predictable loading states, and REST-backed lists that fail visibly instead of spinning forever.
A few rules I keep:
- Show skeleton or empty states, not a blank scaffold.
- Map API errors to human copy in the presentation layer.
- Keep forms close to the domain: validate what the use case needs, not every JSON key.
Multi-platform without duplicating rules
Flutter’s promise is one codebase. DDD makes that promise real. The same checkout or inventory rule can drive Android, iOS, and a desktop build if it lives in the domain, not in a build() method.
That is also why I care about the backend contract. A messy API forces the UI to invent business logic. A documented API plus generated clients lets Flutter stay a client.
See the related Flutter project on this site, or reach out if you need a mobile client that will not collapse when the OpenAPI file grows.
Frequently Asked Questions
Why Domain-Driven Design in Flutter?
DDD keeps business rules out of widgets. The domain stays stable while UI and generated HTTP clients change with the backend.
What does OpenAPI generation replace?
Hand-written DTOs and API methods. When Swagger changes, you regenerate models and clients instead of chasing field names in every screen.
Does DDD slow a small mobile app down?
A light DDD split is enough: domain entities, use cases, and adapters. You do not need a huge enterprise folder tree on day one.
Related articles
Production UIs with React, Angular, and Tailwind
How I build fast, consistent production UIs with React, Angular, and Tailwind without turning CSS into a second codebase.

My path as a full-stack web and mobile developer
How I went from full-stack developer training to shipping Spring Boot APIs, Flutter apps, and CI pipelines clients can trust.