# ADR-005: Feature Folder Organization for Controls ## Status Accepted ## Context With 77+ controls in the library, code organization becomes critical for maintainability. The structure must support: - Clear isolation between controls - Easy location of control-related files - Logical grouping of complex control components - Scalability as new controls are added ## Decision ### Feature Folder per Control Each control resides in `Controls/{ControlName}/` directory: ``` Controls/ ├── Button/ │ ├── Button.cs │ └── Button.xaml ├── Card/ │ ├── Card.cs │ └── Card.xaml ├── NavigationView/ │ ├── NavigationView.Base.cs │ ├── NavigationView.Properties.cs │ ├── NavigationView.Events.cs │ ├── NavigationView.Navigation.cs │ ├── NavigationView.TemplateParts.cs │ ├── NavigationView.AttachedProperties.cs │ ├── NavigationView.xaml │ ├── NavigationViewItem.cs │ ├── NavigationViewItemHeader.cs │ └── NavigationViewItemSeparator.cs ``` **One control = one folder** with all related files. ## Structure Patterns ### Simple Controls **Pattern:** Single .cs + .xaml pair ``` Badge/ ├── Badge.cs # Control implementation └── Badge.xaml # Implicit style ``` **Applies to:** Button, Badge, Card, InfoBar, TextBox, etc. (50+ controls) ### Complex Controls with Partial Classes **Pattern:** Multiple partial class files organized by concern ``` NavigationView/ ├── NavigationView.Base.cs # Core control logic ├── NavigationView.Properties.cs # 27 dependency properties ├── NavigationView.Events.cs # 7 routed events ├── NavigationView.Navigation.cs # Page navigation logic ├── NavigationView.TemplateParts.cs # Template part fields/binding ├── NavigationView.AttachedProperties.cs # Attached property definitions └── NavigationView.xaml # Implicit style + template ``` **Partial class naming:** `{ControlName}.{Concern}.cs` **Applies to:** NavigationView, ContentDialog, TitleBar ### Controls with Related Types **Pattern:** Additional related controls in same folder ``` NavigationView/ ├── NavigationView*.cs # Main control (6 files) ├── NavigationView.xaml ├── NavigationViewItem.cs # Item container ├── NavigationViewItemHeader.cs # Header item ├── NavigationViewItemSeparator.cs # Separator ├── NavigationViewContentPresenter.cs # Content host ├── INavigationView.cs # Interface └── INavigationViewItem.cs # Item interface ``` **Rationale:** Tightly coupled types that are only used together. ### Controls with Supporting Subdirectories **Pattern:** Subfolder for supporting types ``` ContentDialog/ ├── ContentDialog.cs ├── ContentDialog.FocusBehavior.cs ├── ContentDialog.xaml ├── ContentDialogHost.cs ├── ContentDialogHostBehavior.cs └── EventArgs/ ├── ContentDialogButtonClickEventArgs.cs ├── ContentDialogClosingEventArgs.cs └── ContentDialogClosedEventArgs.cs ``` **When to use subfolder:** - 3+ related types (EventArgs, Converters, Enums) - Clear sub-component (e.g., EventArgs) ## Flat Namespace Strategy **All controls use:** `Wpf.Ui.Controls` namespace (regardless of folder depth) ```csharp // File: Controls/NavigationView/NavigationView.cs namespace Wpf.Ui.Controls; // Flat namespace, not Wpf.Ui.Controls.NavigationView // ReSharper disable once CheckNamespace ``` **Trade-off:** Folder structure does not match namespace. **IDE Configuration Required:** ```ini # .editorconfig dotnet_diagnostic.IDE0130.severity = none # Suppress namespace/folder mismatch ``` ## Partial Class Decomposition Strategies ### By Concern NavigationView splits by logical concern: - **Base.cs** - Control infrastructure (template application, initialization) - **Properties.cs** - Dependency property definitions - **Events.cs** - Routed event definitions - **Navigation.cs** - Page navigation logic - **TemplateParts.cs** - Template part fields and OnApplyTemplate logic - **AttachedProperties.cs** - Attached property definitions ### By Feature ContentDialog splits by feature: - **ContentDialog.cs** - Main implementation (async ShowAsync, dialog lifecycle) - **ContentDialog.FocusBehavior.cs** - Keyboard focus management (isolated behavior) ### Guidelines for Splitting **When to split:** - File exceeds 500 lines - Clear separation of concerns exists (properties vs. logic) - Feature is self-contained and isolatable **How to name:** - **Base.cs** - Core control logic (template, initialization) - **Properties.cs** - All dependency properties - **Events.cs** - All routed events - **{Feature}.cs** - Specific feature (FocusBehavior, Animation, etc.) **Don't split:** - Controls under 300 lines - No clear separation of concerns - When references would create circular dependencies ## File Naming Conventions ### Control Classes ``` {ControlName}.cs # Simple control {ControlName}.{Concern}.cs # Partial class with concern ``` ### XAML Styles ``` {ControlName}.xaml # Implicit style ResourceDictionary ``` ### Supporting Types ``` {TypePurpose}{ControlName}.cs # e.g., NavigationViewItem {ControlName}{Purpose}.cs # e.g., ContentDialogHost I{ControlName}.cs # Interface ``` ### Event Arguments ``` {ControlName}{EventName}EventArgs.cs ``` Examples: - `ContentDialogButtonClickEventArgs.cs` - `NavigatedEventArgs.cs` ## Enforcement ### MUST Follow 1. **One control = one folder** under `Controls/` 2. **Paired .cs + .xaml** with matching names 3. **Flat namespace** `Wpf.Ui.Controls` for all controls 4. **Partial class naming** `{ControlName}.{Concern}.cs` 5. **ReSharper suppress comment** when namespace doesn't match folder: ```csharp namespace Wpf.Ui.Controls; // ReSharper disable once CheckNamespace ``` 6. **Keep related types together** in same folder when tightly coupled ### MUST NOT Do 1. **Never nest control folders** (keep flat under Controls/) ``` ❌ Controls/Buttons/Button/ ✅ Controls/Button/ ``` 2. **Never use category-specific namespace** ```csharp ❌ namespace Wpf.Ui.Controls.Buttons; ✅ namespace Wpf.Ui.Controls; ``` 3. **Never split files arbitrarily** (must have clear separation of concerns) 4. **Never create more than 2 directory levels** under Controls/ ``` ✅ Controls/ContentDialog/EventArgs/ ❌ Controls/ContentDialog/EventArgs/Closing/ ``` ### Verification 1. **IDE0130 suppressed** in .editorconfig for namespace/folder mismatch 2. **Code review** checks folder organization 3. **Naming conventions** enforced during PR review ## Benefits ### Developer Experience **Easy to Find:** ```bash # Looking for Button control? Controls/Button/Button.cs # Immediately obvious location ``` **Clear Boundaries:** - All Button-related code in `Controls/Button/` - No cross-control dependencies (enforced by folder isolation) **Scalable:** - Adding new control = create new folder - Doesn't affect existing controls ### Maintainability **Isolation:** - Changes to Button don't affect Card - Merge conflicts localized to single control **Discoverability:** - New developers find controls by folder name - Related types co-located (NavigationViewItem with NavigationView) **Refactoring:** - Easy to split large files (add `.Properties.cs`) - Clear when to split (file size, concern separation) ### Build Performance **Partial Classes:** - Compiler can parallelize partial class compilation - Changes to Properties.cs don't trigger recompilation of Navigation.cs ## Trade-offs ### Advantages - ✅ Clear physical organization - ✅ Easy to locate control files - ✅ Enforces encapsulation (hard to reference across control folders) - ✅ Scales to 100+ controls - ✅ Supports complex controls with many files ### Disadvantages - ❌ Folder/namespace mismatch requires suppression - ❌ 77+ folders in single directory (large directory) - ❌ No physical categorization (all controls appear equal) - ❌ Related controls may be far apart alphabetically (Button vs. ToggleButton) ## Alternatives Considered ### Category-Based Folders ``` Controls/ ├── Buttons/ │ ├── Button/ │ └── ToggleButton/ └── Navigation/ └── NavigationView/ ``` **Rejected:** - Requires category-specific namespaces or deeper mismatch - Category classification is subjective (is ToggleSwitch a Button or Input?) - Doesn't align with flat namespace strategy ### Single File Per Control ``` Controls/ ├── Button.cs ├── Button.xaml ├── NavigationView.cs └── NavigationView.xaml ``` **Rejected:** - Complex controls (NavigationView 1000+ lines) unmanageable - Supporting types have unclear location - 150+ files in single directory ### Hybrid Categorization ``` Controls/ ├── Button/ ├── Input/ │ ├── TextBox/ │ └── NumberBox/ └── Navigation/ └── NavigationView/ ``` **Rejected:** - Inconsistent structure (some categories, some not) - Unclear where new controls go - Complicates namespace strategy ## Migration Path ### Adding New Simple Control 1. Create folder `Controls/{NewControl}/` 2. Add `{NewControl}.cs` with control class 3. Add `{NewControl}.xaml` with implicit style 4. Add ReSharper suppress comment to .cs file ### Splitting Existing Control Example: Card becomes too large **Before:** ``` Card/ ├── Card.cs (500 lines) └── Card.xaml ``` **After:** ``` Card/ ├── Card.Base.cs (200 lines - core logic) ├── Card.Properties.cs (100 lines - dependency properties) ├── Card.Animation.cs (100 lines - animation logic) └── Card.xaml ``` **Refactoring steps:** 1. Extract dependency properties to `Card.Properties.cs` 2. Extract animation logic to `Card.Animation.cs` 3. Keep core logic in `Card.Base.cs` 4. All files use `partial class Card` ## Documentation ### Control Folder README Each complex control folder includes README.md: ```markdown # NavigationView Complex navigation container with 6 partial class files: - **Base.cs** - Core control logic, template application - **Properties.cs** - 27 dependency properties - **Events.cs** - 7 routed events - **Navigation.cs** - Page navigation, journal, back/forward - **TemplateParts.cs** - Template part bindings - **AttachedProperties.cs** - HeaderContent attached property Related types: - NavigationViewItem - Selectable item container - NavigationViewItemHeader - Non-selectable header - INavigationView - Public control interface ``` ## References - [Feature Folders in ASP.NET](https://docs.microsoft.com/archive/msdn-magazine/2016/september/asp-net-core-feature-slices-for-asp-net-core-mvc) (similar pattern) - [Vertical Slice Architecture](https://jimmybogard.com/vertical-slice-architecture/)