Skip to content

Source Code — Solution Structure

Where every kind of file lives, how the five projects reference each other, and the two layout traps that cost the most time: namespaces do not follow folders, and two folder names describe something other than their contents.

Read alongside Architecture — Project Structure, which covers the same projects from a design angle rather than a navigation one.

The five projects

Solution file: NKO.Main/ERP-System.sln. Startup project: NKO.Main/NKO.Main.

Project Path .cs Output
NKO.Main NKO.Main/NKO.Main/ 149 The deployable web application
NKO.Shared NKO.Lib/NKO.Shared/ 131 Class library — identity, permissions, shared BAL, embedded views
NKO.DataAccess NKO.Lib/NKO.DataAccess/ 183 Class library — core EF model (140 entities, 18 migrations)
NKO.Warehouse.DataAccess NKO.Lib/NKO.Warehouse.DataAccess/ 33 Class library — warehouse EF model (21 entities, 5 migrations)
NKO.Helpers NKO.Main/NKO.Helpers/ 1 Class library — one validation attribute

Reference graph

flowchart TD
    MAIN["NKO.Main<br/>web application"]
    SHARED["NKO.Shared"]
    DATA["NKO.DataAccess"]
    WARE["NKO.Warehouse.DataAccess"]
    HELP["NKO.Helpers"]

    MAIN --> DATA
    MAIN --> SHARED
    MAIN --> WARE
    SHARED --> DATA
    SHARED --> WARE
    DATA --> HELP

Read from NKO.Main.csproj and the four library .csproj files. NKO.Warehouse.DataAccess and NKO.Helpers are leaves — neither references anything.

The folder layout contradicts the dependency direction

NKO.Helpers is the lowest-level library in the graph — NKO.DataAccess depends on it — yet it sits physically under NKO.Main/, next to the web application, while the three libraries it sits below live under NKO.Lib/. Nothing breaks; it just means the folder tree is not a reliable guide to layering.

Build settings

All five projects target net6.0. Two settings are enabled in exactly one project:

Setting NKO.Helpers The other four
<Nullable> enable (not set — disabled)
<ImplicitUsings> enable (not set — disabled)

So the one-file project has modern C# defaults and the 496-file rest of the solution does not. A file moved between them changes meaning: nullable reference warnings appear or vanish, and using System; becomes required. Tracked as R8-5.

NKO.Main — the web application

NKO.Main/NKO.Main/
├── Controllers/            29 files · core controllers
├── WarehouseControllers/   10 files · warehouse controllers
├── BAL/                    32 files · core business logic  ← the rules live here
├── WareBAL/                 8 files · warehouse business logic
├── ViewModels/             61 files · grouped by feature
├── Views/                 488 .cshtml · one folder per controller
├── Helpers/                 1 file  · OperationConstants.cs
├── Infrastructure/          3 files · IHostedService jobs + the DI extension
├── ScheduledTasks/          2 files · Quartz IJob implementations
├── Resources/              .resx localisation (en / ar / tr)
├── wwwroot/                css, js, images, Rotativa binaries
├── Program.cs              host builder + Quartz registration
└── Startup.cs              DI container and middleware pipeline

Warehouse code is separated by folder, not by project. WarehouseControllers/ and WareBAL/ sit inside NKO.Main but talk to a different database through a different DbContext. Nothing at compile time stops core code calling warehouse code or the reverse — the boundary is a convention.

NKO.Shared — the cross-cutting library

NKO.Lib/NKO.Shared/
├── Controllers/     Account, Common, Files, Culture + the two ViewComponents
├── BAL/              7 files · BSign, Bsettings, BCalendar, SharedBAL,
│                     AttenadancesHandler, BArchiveFile, Permissionchecker
├── Helpers/          BUser.cs (1 909 ln), CommonConstants.cs (826),
│   │                 Enums.cs (972), ExportHelper.cs (1 111),
│   │                 NextUserHelper.cs (668), Utils.cs (433), EmailManager.cs,
│   │                 BNotification.cs, Security.cs, SiteSettings.cs
│   ├── Sec/          ClaimRequirementFilter, PermissionsHandler, HasPermission,
│   │                 BCryptPasswordHasher, ApplicationUserManager, ClaimsProvider
│   ├── Contracts/    CurrentRequestData, IS3ServiceHandler, IStorageHandler, …
│   ├── Leave/        LeaveSetting, LeaveTypeCodes
│   └── Evaluation/   EvaluationStatus
├── Configs/          BaseAppSettings, StorageConfigs, S3ServiceHandler,
│                     AttendanceConfigs, ScheduledJobsSettings
├── Models/          12 files · ApplicationUser and identity models
├── ViewModels/       incl. Training/TrainingStatus.cs — see the trap below
├── Attributes/       1 custom MVC attribute
├── Hangfire/         ← contains SignalR, not Hangfire (see below)
└── Views/           29 .cshtml, served as embedded resources

This project is why authentication and permissions are available to both the core and the warehouse side: both reference it, and its views are embedded in the assembly and surfaced through EmbeddedFileProvider.

The two data-access projects

NKO.Lib/NKO.DataAccess/                NKO.Lib/NKO.Warehouse.DataAccess/
├── DbModel/    140 entities           ├── DbModel/    15 entities
│   └── (+ NKO_DBEntities1.cs)         │   ├── Assets/  6 more entities
└── Migrations/ 18 migrations          │   └── (+ WarehouseDBContext.cs)
    (37 files: 18 × 2 + snapshot)      └── Migrations/ 5 migrations

Entity counts are DbSet<> properties on each context. The warehouse DbModel/Assets/ sub-folder is easy to miss — it holds Asset, AssetDetail, AssetPendingAmount, AssetsReturn, Computer and GRNReport, which is why the warehouse total is 21, not 15.

Counting migrations

Migrations/ holds 37 files for the core model, but that is 18 migrations × 2 files (<name>.cs + <name>.Designer.cs) plus one ModelSnapshot. The migration count is 18.

Namespaces do not follow folders

This is the single most disorienting thing about the codebase. A file's folder tells you almost nothing about its namespace, and the same folder can hold several unrelated ones.

File Folder suggests Actual namespace
NKO.Shared/Helpers/BUser.cs NKO.Shared.Helpers NKO.BAL
NKO.Shared/BAL/BSign.cs NKO.Shared.BAL NKO.BAL
NKO.Shared/Helpers/Sec/ClaimRequirementFilter.cs NKO.Shared.Helpers.Sec NKO.Shared.Web.Helpers
NKO.Shared/Helpers/Contracts/CurrentRequestData.cs NKO.Shared.Helpers.Contracts NKO.Shared.Logic.Contracts
NKO.Shared/Configs/BaseAppSettings.cs NKO.Shared.Configs NKO.Shared.Logic.Configs
NKO.Shared/Configs/S3ServiceHandler.cs NKO.Shared.Configs NKO.Shared.Configs
NKO.Shared/Controllers/AccountController.cs NKO.Shared.Controllers NKO.Shared
NKO.Shared/Hangfire/RealTime.cs NKO.Shared.Hangfire NKO.Shared.Logic

Note Configs/ in particular: BaseAppSettings.cs and S3ServiceHandler.cs are in the same folder under two different namespaces. There are also two namespaces named Logic and Web that have no corresponding folder anywhere — and one file, Controllers/AttendancesController.cs, still declares namespace IYD.Controllers, from a different product entirely.

The practical consequences: a class cannot be located from its using statement, "go to folder" and "go to namespace" give different answers, and adding a file to a folder does not tell you which namespace to declare — you must copy a neighbour, and neighbours disagree. Tracked as R8-1.

Namespace spread

The same kind of code is split across unrelated namespace roots:

Kind Namespaces used
Controllers NKO.Controllers (24), NKO.Warehouse.Controllers (10), NKO.Main.Controllers (5), NKO.Shared (5), IYD.Controllers (1)
Business logic NKO.BAL (23), NKO.Main.BAL (14), NKO.Warehouse.BAL (8), NKO.Infrastructure (1), NKO.Shared.BAL (1)
Entities NKO.DataAccess.DbModel (142), NKO.Warehouse.DataAccess (19), NKO.Warehouse.DataAccess.DbModel.Assets (3), NKO.DataAccess.DbModel.Base (2)

Folder names that lie

Two folders contain something other than what they are named. Both are easy to lose an hour to.

Path Name implies Actually contains
NKO.Shared/Hangfire/ Hangfire configuration SignalR. RealTime.cs defines the RealTime : Hub and RealTimeNotifier; CustomUserIdProvider.cs implements SignalR's IUserIdProvider. No Hangfire code is in this folder — Hangfire is configured inline in Startup.cs. Tracked as R8-2.
NKO.Shared/ViewModels/Training/TrainingStatus.cs Training statuses Seven enums for different modules — TrainingStatus, AdvanceStatus, OvertimeStatus, LeaveStatus, UpgradeStatus, PlannedTypes, TrainingNeedTypes. Tracked as R7-15.

To this add the two files whose names carry a trailing space before .cs — AdvanceController .cs and TrainingController .cs (R7-14). They compile, but they break path-matching scripts and some tooling.

Where to start reading

If you are changing… Start at
A screen's layout Views/<Controller>/<Action>.cshtml
What a screen shows the action in Controllers/<X>Controller.cs, then the B* call it makes
A business rule BAL/B<Entity>.cs — almost always one class
An approval step NextUserHelper.Get<Doc>NextUser + the Set…Accepted / Set…Refused methods on that B* class
A permission the [ClaimRequirement("Key")] attribute, then AppPermission rows in the database
A constant or status NKO.Shared/Helpers/CommonConstants.cs (58 static classes) or Enums.cs (59 enums)
Anything warehouse WareBAL/ + WarehouseControllers/, against WarehouseDBContext