In previous posts, we introduced LumenUI as a modular, CLI-based tool built to solve the problem of dependency bloat in Flutter apps. LumenUI empowers developers to generate only the UI components they need โ keeping apps fast, lightweight, and maintainable.
But today, we're taking a step back to show you how it all works internally.
This post dives into the architecture of LumenUI, showing how a clean layered architecture helps the tool remain scalable, organized, and easy to evolve โ all of which are essential qualities in open-source tooling.
๐งฑ The Three-Layer Architecture of LumenUI
LumenUI follows a three-layer architecture, which divides the system into logical layers: Presentation, Business Logic, and Data Access. This is a simplified version of the well-known N-tier architecture adapted to the needs of a CLI tool.
Let's explore what each layer does and why it matters.
1๏ธโฃ Presentation Layer โ CLI Interface Module
This is the interface layer that users interact with through the terminal. It handles all command-line parsing and feedback to the user.
Responsibilities:
- โขReading commands like --type, --name, --output
- โขDisplaying help, version info, and error messages
- โขValidating whether required inputs are provided (e.g., both --type and --name)
- โขDelegating the task to the business logic layer
Why It Matters:
The CLI Interface keeps the user experience clean and consistent. It ensures that users are guided correctly when interacting with the tool, and any errors are communicated clearly and immediately.
๐ Example: If the user forgets to provide --name, the presentation layer will instantly respond with an informative error message.
2๏ธโฃ Business Logic Layer โ Core Operations
This is the core of LumenUI โ the layer where the real decision-making happens.
Submodules:
- Validation Service:Ensures that inputs follow correct naming conventions and supported types.
- Component Generator:Coordinates how the component should be generated based on type, name, and options.
- Template Manager:Loads templates, applies custom settings, and prepares them for rendering.
Why It Matters:
By isolating logic in this layer, LumenUI ensures clean separation between input parsing and file handling. This makes it easier to extend the tool in the future โ for example, adding new component types, supporting layouts, or integrating plugin systems โ without breaking the CLI or storage logic.
๐ง Example: Want to support a new component like dialog? All the logic goes here. The CLI doesn't need to change at all.
3๏ธโฃ Data Access Layer โ Template Storage
This layer handles interaction with the file system and the templates stored locally.
Responsibilities:
- โขReading stored UI templates (e.g., for buttons, cards)
- โขWriting generated files to the appropriate directories in the Flutter project
- โขManaging file paths, output directories, and user-defined configurations (in the future)
Why It Matters:
Separating data access ensures the generator logic doesn't deal with file management directly. This abstraction is especially useful for testing and future-proofing the system.
๐ก Example: You want to load a different template set based on the theme or platform (Material or Cupertino). This logic can live entirely within the data access layer.
๐ Real-World Workflow in LumenUI
Let's walk through a typical use case with this architecture in action:
Command:
Flow:
- Presentation Layer captures the CLI command and parses the arguments.
- Validation Service checks if button is a supported component and whether primary_button follows naming rules.
- Component Generator creates the scaffolding logic and passes the request to the Template Manager.
- Template Manager loads the corresponding template.
- Data Access Layer saves the final generated file in the lib/ui directory.
Each layer completes its task independently but cohesively, resulting in a smooth and maintainable process.
๐ Benefits of Layered Architecture in LumenUI
The decision to use layered architecture wasn't random. It brings major advantages that align with the goals of LumenUI:
โ Separation of Concerns
Each layer has a clear responsibility. This simplifies maintenance and reduces the risk of bugs when making updates.
๐ Extensibility
You can easily add new component types or support theming without changing how commands are handled or how files are written.
๐งช Testability
Each layer can be unit-tested independently:
- โข CLI logic for parsing and messaging
- โข Generator logic for rules and decisions
- โข Template manager for formatting
- โข File operations for read/write integrity
๐ Reusability
Templates and generators can be reused across projects or plugged into other tools in the future.
๐ Stability
By keeping layers loosely coupled, the system is less prone to breakage when one part is updated or extended.
๐ฎ What's Coming Next
This architecture is just the foundation. Here are some features we're working on that will build on top of this structure:
- ๐Custom Templates: Let users add their templates and inject them via config files.
- ๐จTheme Support: Dynamic styling and theming are built into the generator.
- ๐Plugin System: Third-party developers can contribute new components, layouts, or features.
- ๐ฑProject-wide Scaffolds: Commands like generate screen or generate layout will scaffold multiple UI parts at once.
The architecture we've built ensures we can support all of these without breaking existing functionality.