Architecture Diagram
Three-Layer Architecture
Queryly follows a layered architecture pattern with clear separation of concerns:1. CLI Layer
Location:src/Queryly.CLI/
The presentation layer that handles user interaction and command processing.
Responsibilities:
- Command-line argument parsing
- User input/output via Spectre.Console
- Command routing and orchestration
- Configuration file management
Program.cs- Application entry pointCommands/- Command implementations (connect, schema, data)Configuration/- Config helpers for reading/writing connections.json
2. Core Layer
Location:src/Queryly.Core/
The business logic layer that defines contracts and core functionality.
Responsibilities:
- Define data models and interfaces
- Connection management logic
- Schema exploration logic
- Query execution logic
- Exception handling
Models/- Data models (ConnectionInfo, TableInfo, ColumnInfo, DatabaseType)Connections/- IConnectionProvider interfaceQuery/- Query execution logicExceptions/- Custom exceptions
3. Providers Layer
Location:src/Queryly.Providers/
The data access layer with database-specific implementations.
Responsibilities:
- Implement IConnectionProvider for each database
- Handle database-specific SQL syntax
- Manage database-specific connection types
- Query database metadata (tables, columns, schemas)
Sqlite/- SQLite implementationSqlServer/- SQL Server implementationPostgreSql/- PostgreSQL implementationMySql/- MySQL implementation
Project Structure
Technology Stack
Queryly leverages modern .NET technologies and best-in-class libraries:Core Technologies
- Language: C# 12
- Runtime: .NET 8
- Database Access: ADO.NET, Dapper
- Terminal UI: Spectre.Console
- Configuration: System.Text.Json
Database Providers
- SQLite: Microsoft.Data.Sqlite
- SQL Server: Microsoft.Data.SqlClient
- PostgreSQL: Npgsql
- MySQL: MySql.Data
Testing
- Test Framework: xUnit
- Mocking: Moq
- Assertions: FluentAssertions
Component Interaction
Connection Flow
Data Flow Diagram
Dependency Flow
Queryly follows strict dependency rules:Dependency Inversion: The Core layer defines interfaces (IConnectionProvider), and the Providers layer implements them. The CLI layer depends only on Core abstractions, not concrete implementations.
Key Design Patterns
1. Provider Pattern
Each database has its own provider implementingIConnectionProvider:
2. Factory Pattern
Providers are instantiated based onDatabaseType:
3. Repository Pattern
Connection configurations are stored and retrieved from JSON:4. Command Pattern
Each CLI command is a separate class that handles specific functionality:ConnectAddCommandConnectListCommandSchemaListCommandDataBrowseCommand- etc.
Design Principles
Separation of Concerns
- CLI: User interaction only
- Core: Business logic and contracts
- Providers: Database-specific implementation
Single Responsibility
Each provider handles one database type. Each command handles one user operation.Open/Closed Principle
New databases can be added by implementingIConnectionProvider without modifying existing code.
Dependency Inversion
High-level modules (CLI) depend on abstractions (IConnectionProvider), not concrete implementations.Error Handling Strategy
Exception Hierarchy
- Custom exceptions in
Queryly.Core/Exceptions/ - Database-specific exceptions wrapped in meaningful messages
- User-friendly error messages displayed via CLI
Example
Configuration Management
Storage Location
- Windows
- macOS/Linux
Configuration Format
Performance Considerations
Fast Startup
- Minimal dependencies
- Lazy loading of providers
- Configuration loaded on-demand
Efficient Queries
- Use Dapper for lightweight ORM
- Parameterized queries to prevent SQL injection
- Connection pooling via ADO.NET
Memory Management
usingstatements for proper disposal- Async/await for non-blocking I/O
- Streaming large result sets
Extensibility Points
Adding New Databases
ImplementIConnectionProvider for the new database type.
See Adding Providers for details.
Adding New Commands
Create a new command class inQueryly.CLI/Commands/.
Adding New Export Formats
Extend the export logic in the Core layer.Security Considerations
Connection Strings
- Stored in local user directory
- File permissions restrict access to current user
- No encryption (relies on OS-level security)
SQL Injection Prevention
- Parameterized queries throughout
- No direct string concatenation for user input
- Provider implementations sanitize table/column names
Testing Strategy
Unit Tests
- Test individual components in isolation
- Mock database connections
- Verify business logic correctness
Integration Tests
- Test against real databases (SQLite in-memory)
- Verify provider implementations
- Test end-to-end command flows