# .NET Project Structure

## 1. Solutions, Projects, Assemblies

| Thing | File | What it is |
|---|---|---|
| **Solution** | `*.sln` / `*.slnx` | a list of projects that are opened/built together. Not a build unit itself. |
| **Project** | `*.csproj` | MSBuild file: what to compile, target framework, dependencies. One project → one assembly. |
| **Assembly** | `*.dll` / `*.exe` | compiled IL + metadata — the unit of deployment, versioning and `internal` visibility. |
| **Namespace** | `namespace X.Y;` | a naming scope only. Unrelated to folders or assemblies (by convention it mirrors them). |

A typical layered backend:

```
Shop.sln
src/
  Shop.Api/            Shop.Api.csproj           (ASP.NET Core host, controllers)
  Shop.Application/    Shop.Application.csproj   (use cases, interfaces)
  Shop.Domain/         Shop.Domain.csproj        (entities, no dependencies)
  Shop.Infrastructure/ Shop.Infrastructure.csproj(EF Core, external APIs)
tests/
  Shop.Domain.Tests/   Shop.Domain.Tests.csproj  (xUnit)
Directory.Build.props   shared MSBuild settings for every project below it
global.json             pins the SDK version
```

## 2. The SDK-Style `.csproj`

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>              <!-- Library (default) or Exe -->
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <RootNamespace>Shop.Api</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
    <ProjectReference Include="..\Shop.Application\Shop.Application.csproj" />
  </ItemGroup>
</Project>
```

SDK-style projects **glob** every `**/*.cs` under the folder automatically — you don't list source files. `Sdk="Microsoft.NET.Sdk.Web"` adds ASP.NET Core references and defaults.

## 3. The CLI Workflow

```bash
dotnet new sln -n Shop
dotnet new classlib -o src/Shop.Domain
dotnet new webapi   -o src/Shop.Api
dotnet new xunit    -o tests/Shop.Domain.Tests
dotnet sln add src/Shop.Domain src/Shop.Api tests/Shop.Domain.Tests

dotnet add src/Shop.Api reference src/Shop.Domain     # ProjectReference
dotnet add src/Shop.Api package Serilog.AspNetCore     # PackageReference

dotnet restore   # resolve NuGet graph -> obj/project.assets.json
dotnet build     # compile -> bin/Debug/net8.0/
dotnet test
dotnet run --project src/Shop.Api
dotnet publish -c Release -o out   # deployable output
```

## 4. Dependency Direction

Project references form a **directed acyclic graph** — MSBuild refuses cycles. That makes the `.csproj` graph a cheap, compiler-enforced architecture rule: if `Shop.Domain` has no references, domain code *cannot* accidentally depend on EF Core or ASP.NET.

```
Api ──► Application ──► Domain
 └────► Infrastructure ─┘ (implements Application's interfaces)
```

## 5. Visibility Across Assemblies

`internal` means "visible inside this assembly only." Tests sometimes need internals:

```xml
<ItemGroup>
  <InternalsVisibleTo Include="Shop.Domain.Tests" />
</ItemGroup>
```

## 6. Configuration and Entry Point

Modern apps use **top-level statements** in `Program.cs` (the compiler generates the `Main` method) and layered configuration: `appsettings.json` → `appsettings.{Environment}.json` → environment variables → command line, later sources overriding earlier ones.

```csharp
var builder = WebApplication.CreateBuilder(args);
var conn = builder.Configuration.GetConnectionString("Default");
var app = builder.Build();
app.MapGet("/health", () => "ok");
app.Run();
```

## Under the Hood: Restore, Build, and What Lands in `bin/`

1. **Restore** walks every `PackageReference` and `ProjectReference` transitively, resolves one version per package (nearest-wins; the lowest version that satisfies the range), downloads into the global cache (`~/.nuget/packages`) and writes `obj/project.assets.json` — the lock-like graph the build reads.
2. **Build** runs MSBuild targets: `CoreCompile` invokes **Roslyn** (`csc`) with every source file and the resolved reference assemblies, producing an IL assembly in `obj/`, then copies it plus dependencies into `bin/<Configuration>/<TFM>/`.
3. Alongside your `.dll`, the output has `*.deps.json` (dependency manifest the host uses to locate assemblies) and, for executables, `*.runtimeconfig.json` (which runtime version to load) and an apphost (`Shop.Api` / `Shop.Api.exe`).
4. At run time the **host** (`dotnet` muxer → `hostfxr` → `hostpolicy`) reads those JSON files, loads the CoreCLR, and the **JIT** compiles IL methods to native code the first time each is called.

![dotnet restore resolves the NuGet graph into obj/project.assets.json, Roslyn compiles sources into an IL assembly, and the host uses deps.json and runtimeconfig.json to load the runtime and JIT the IL](diagrams/01-build-pipeline.svg)

## Common Pitfalls

1. **Circular project references** — MSBuild errors out. Extract the shared piece into a lower project or introduce an interface in the lower layer.
2. **Mismatched `TargetFramework`s** — a `net8.0` project can't reference a `net9.0` library.
3. **Editing files under `bin/` or `obj/`** — they're regenerated; never commit them (`dotnet new gitignore`).
4. **Package versions drifting between projects** — use **Central Package Management** (`Directory.Packages.props` + `ManagePackageVersionsCentrally`) so every project agrees.
5. **Putting secrets in `appsettings.json`** — use user-secrets in dev and environment variables / a vault in production.

## Quick Reference

| Command | Effect |
|---|---|
| `dotnet new <template> -o <dir>` | scaffold a project |
| `dotnet sln add <proj>` | add to solution |
| `dotnet add reference <proj>` | project-to-project dependency |
| `dotnet add package <id>` | NuGet dependency |
| `dotnet build` / `test` / `run` | compile / test / execute |
| `dotnet publish -c Release` | deployable output |
| `Directory.Build.props` | shared settings for a folder tree |
| `global.json` | pin the SDK version |
