# Project Management MVP: Day 2

Yesterday, we laid down our baseline repository foundation: a clean monorepo housing both Angular and .NET, backed by local cross-platform emulators. Today, we move into the heart of our data tier — designing a highly optimized NoSQL schema in **Azure Cosmos DB**, mapping out domain models, implementing the **Repository Pattern** in C#, and wiring up our runtime dependencies.

Along the way, we hit a few classic compilation roadblocks. Here is how we built our data engine and overcame real-world development friction.

* * *

### Strategy: Why Cosmos DB Over Relational SQL?

For a modern project management platform, choosing a NoSQL architecture over a traditional relational database (like SQL Server) provides specific engineering advantages:

*   **Flexible JSON Modeling:** Tasks aren't static. They require dynamic custom tags, variable sub-task arrays, and shifting metadata. NoSQL handles this without requiring destructive schema migrations.
    
*   **Horizontal Scalability via Partitioning:** Cosmos DB forces you to design for scale on day one. By deliberately picking a partition key, we guarantee our data scales horizontally across physical partitions seamlessly.
    
*   **Cost Efficiency (Free Tier Optimization):** Azure gives us an excellent **Always Free Tier** allowance of 1,000 RU/s (Request Units) of throughput and 25 GB of storage. By using a **Shared Throughput Database**, we can host multiple containers under a single cost envelope without spending a cent.
    

* * *

### Step 1: Real-World Schema & Partitioning Design

To model our entities without breaking NoSQL design principles, we establish two targeted containers inside our shared-throughput database:

#### Container 1: `WorkspacesAndTasks` (Partition Key: `/workspaceId`)

We co-locate both Workspace metadata and individual Tasks inside the same container. For the Workspace document itself, its `id` doubles as its `workspaceId`.

#### Container 2: `Users` (Partition Key: `/id`)

Because users belong across multiple distinct workspaces globally, assigning them a workspace partition key would cause unnecessary data duplication. Isolating them into their own container makes lookup clean and direct.

> **AZ-204 Exam Insight:** Notice how every task contains `workspaceId`. By executing queries like `"SELECT * FROM c WHERE c.type = 'task'"` and passing the `workspaceId` as the partition key context, Azure targets a **single physical partition**. This avoids costly, slow cross-partition scans, making our Kanban board fetches fast and highly cost-efficient.

* * *

### Step 2: C# Domain Modeling (`System.Text.Json` Alignment)

We built out strongly typed C# domain models to map directly to these JSON definitions. Because Cosmos DB requires a lowercase string property named `"id"` for item tracking, we use `[JsonPropertyName]` attributes to align our clean C# naming styles with NoSQL engine specifications.

**The Task Model Example (**`Models/TaskItem.cs`**):**

```csharp
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace backend.Models
{
    public class TaskItem
    {
        [JsonPropertyName("id")]
        public string Id { get; set; } = Guid.NewGuid().ToString();

        [JsonPropertyName("workspaceId")]
        public string WorkspaceId { get; set; }

        [JsonPropertyName("type")]
        public string Type { get; set; } = "task";

        [JsonPropertyName("title")]
        public string Title { get; set; }

        [JsonPropertyName("status")]
        public string Status { get; set; }

        [JsonPropertyName("tags")]
        public List<string> Tags { get; set; } = new();

        [JsonPropertyName("assignedTo")]
        public string AssignedTo { get; set; }

        [JsonPropertyName("subTasks")]
        public List<SubTask> SubTasks { get; set; } = new();
    }
}

```

* * *

### Step 3: Implementing the Repository Layer & Clean Architecture Reorg

To keep our architecture decoupled, we don't want Cosmos SDK logic bleeding into our application API layer. We created an abstraction interface (`backend.Interfaces.ITaskRepository`) and isolated the concrete database implementation under an expanded Clean Architecture folder layout: `backend/src/Infrastructure/Repositories/TaskRepository.cs`.

Here is the decoupled implementation using the official `Microsoft.Azure.Cosmos` SDK:

```csharp
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos;
using backend.Interfaces;
using backend.Models;

namespace backend.Infrastructure.Repositories
{
    public class TaskRepository : ITaskRepository
    {
        private readonly Container _container;

        public TaskRepository(CosmosClient client, string databaseId, string containerId)
        {
            _container = client.GetContainer(databaseId, containerId);
        }

        // 1. Optimized Point Read (Fastest, lowest cost operation in Cosmos DB)
        public async Task<TaskItem?> GetTaskAsync(string id, string workspaceId)
        {
            try
            {
                ItemResponse<TaskItem> response = await _container.ReadItemAsync<TaskItem>(
                    id,
                    new PartitionKey(workspaceId)
                );
                return response.Resource;
            }
            catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
            {
                return null;
            }
        }

        // 2. In-Partition Query (Fetches the whole Kanban board cleanly)
        public async Task<IEnumerable<TaskItem>> GetTasksByWorkspaceAsync(string workspaceId)
        {
            var sqlQueryText = "SELECT * FROM c WHERE c.type = 'task'";
            var queryDefinition = new QueryDefinition(sqlQueryText);
            var queryOptions = new QueryRequestOptions { PartitionKey = new PartitionKey(workspaceId) };

            using FeedIterator<TaskItem> feedIterator = _container.GetItemQueryIterator<TaskItem>(
                queryDefinition,
                requestOptions: queryOptions
            );

            var results = new List<TaskItem>();
            while (feedIterator.HasMoreResults)
            {
                FeedResponse<TaskItem> response = await feedIterator.ReadNextAsync();
                results.AddRange(response);
            }
            return results;
        }

        public async Task AddTaskAsync(TaskItem task) =>
            await _container.CreateItemAsync(task, new PartitionKey(task.WorkspaceId));

        public async Task AddWorkspaceAsync(Workspace workspace) =>
            await _container.CreateItemAsync(workspace, new PartitionKey(workspace.WorkspaceId));
    }
}

```

* * *

### Real-World Dev Pitfalls & How We Fixed Them

Moving files around and importing SDK packages often exposes build discrepancies. Here are the two core compilation blockers we solved today:

#### 1\. The Transitive Newtonsoft Build Blocker

Upon importing `Microsoft.Azure.Cosmos` v3 into a modern web API, the compiler threw a major blocker:

> *error : The Newtonsoft.Json package must be explicitly referenced with version >= 10.0.2...*

*   **The Fix:** Even though modern .NET targets `System.Text.Json` natively, the underlying engine of the Cosmos v3 SDK still hooks into `Newtonsoft` for internal serialization checks. Explicitly adding the reference directly to the API via `dotnet add package Newtonsoft.Json` bypassed this build target restriction instantly.
    

#### 2\. The Clean Architecture Namespace Desync

When we pushed files into deeper paths (`src/Infrastructure/Repositories/`), our files fell out of sync, triggering a cascade of `CS0234: The type or namespace name 'Models' does not exist in the namespace 'backend'` errors.

*   **The Fix:** We methodically trace-aligned our namespace ground truths. By establishing `namespace backend.Models` at the entity layer, pulling them into our interfaces via `using backend.Models;`, and linking our repository with `using backend.Interfaces;`, the compiler connected the logical compilation dots perfectly.
    

* * *

### Step 4: Bootstrapping & Dependency Injection in modern .NET 9

With a successful build locked in, we integrated our database connection string settings into `appsettings.json` using the universal local Cosmos DB Emulator master key, then overhauled `Program.cs`.

We registered the `CosmosClient` as a **Singleton** lifecycle (the gold standard for preventing socket exhaustion) and bound `ITaskRepository` as a **Scoped** service. We also activated controller routing support (`AddControllers()` / `MapControllers()`) to gracefully transition away from basic minimal API boilerplate.

```csharp
using Microsoft.Azure.Cosmos;
using backend.Interfaces;
using backend.Infrastructure.Repositories;

var builder = WebApplication.CreateBuilder(args);

// Extract configuration limits securely from appsettings
var cosmosSection = builder.Configuration.GetSection("CosmosDb");
string endpointUrl = cosmosSection["EndpointUrl"] ?? throw new InvalidOperationException("Cosmos EndpointUrl missing.");
string primaryKey = cosmosSection["PrimaryKey"] ?? throw new InvalidOperationException("Cosmos PrimaryKey missing.");
string databaseName = cosmosSection["DatabaseName"] ?? throw new InvalidOperationException("Cosmos DatabaseName missing.");
string containerName = cosmosSection["ContainerName"] ?? throw new InvalidOperationException("Cosmos ContainerName missing.");

// Register unified CosmosClient instance as a Singleton
var cosmosClient = new CosmosClient(endpointUrl, primaryKey, new CosmosClientOptions
{
    SerializerOptions = new CosmosSerializationOptions { PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase }
});
builder.Services.AddSingleton(cosmosClient);

// Inject Repository Abstractions
builder.Services.AddScoped<ITaskRepository>(sp => 
    new TaskRepository(cosmosClient, databaseName, containerName));

builder.Services.AddControllers(); // Enables our upcoming Day 3 routing layer
builder.Services.AddOpenApi();     // Keeps our .NET 9 OpenAPI documentation intact

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.UseHttpsRedirection();
app.MapControllers(); // Directs incoming traffic smoothly to attribute-routed API endpoints
app.Run();

```

* * *

### Architecture Snapshot

Our system layout is evolving predictably. Here is how our data layer separation handles dependencies now:

```mermaid
graph TD
  A[Angular Frontend Client] -->|HTTP Calls| B[.NET Core Web API]
  B -->|Interface Abstraction| C[Task/Workspace Repositories]
  C -->|In-Partition Queries| D[Cosmos DB Emulator: Container Shared Throughput]

```

* * *

### Day 2 Wrap-Up

With Day 2 behind us, our core data framework is solidified:

*   A scalable, dual-container Cosmos DB schema optimized for the Azure Free Tier.
    
*   An intelligent partitioning strategy (`/workspaceId`) to safeguard downstream query performance.
    
*   A professional, Clean Architecture C# repository structure that isolates the Cosmos SDK smoothly.
    
*   A resilient dependency pipeline verified entirely against our local emulator suite.
    

**Coming up next on Day 3:** We will construct our RESTful CRUD controller endpoints in our backend API and layer on the high-performance **Cache-Aside Pattern** using localized memory boundaries to mimic enterprise performance optimization. Stay tuned!
