Unifying Data Design and Code with Entity Framework

A reflection on database-centered system development, CosmosDB's influence, and how Entity Framework unified data design with application code.

Previous article: From Global CSS Chaos to Scoped Isolation

In the previous note, I wrote about style boundaries. This time, I want to focus on data boundaries and why my database design process changed.

This is not a tutorial. It is an architectural reflection based on how my own development practice evolved.

A career built around databases

Most systems I have built in my career have been database systems. Even when requirements looked like screen features, the real behavior of the system was usually decided by how data was stored, related, and updated.

For that reason, data representation was always a core design task for me, not a secondary implementation detail. The format of data and the persistence structure directly affected application structure, service boundaries, and operational safety.

The historical separation between schema and code

For a long time, schema design and application code lived in different places. ER diagrams, table definition documents, and class diagrams were managed as separate artifacts. Implementation then followed those artifacts.

Early in my career, I did not question that separation. It was normal industry practice, and experienced teams adapted to it naturally. Developers became used to manually synchronizing schema changes with application code changes.

It was not elegant, but it was how many teams delivered systems reliably.

My design practice at that time

Before starting implementation, I usually prepared table definitions and ER diagrams. Depending on project complexity, I also added class diagrams or simple object-flow sketches.

The goal was always the same. I wanted to understand data shape first, then trace how it moved through the system.

At that time, I considered this process obvious and never felt strong discomfort with it.

Turning point from a CosmosDB project

My perspective changed during a project where field teams needed to register many kinds of operational reports and consultation records in one system. The requirement was to search across heterogeneous records, not to perform heavy relational aggregation with frequent joins. Because of that, a traditional relational database was not automatically the best fit for the core storage model.

During database evaluation, I encountered CosmosDB, which was still called DocumentDB at that time. I was looking for a storage model that behaved like a registry for diverse records, and CosmosDB matched that intent.

CosmosDB worked well as a JSON document registry. Each record could be stored with a key, and the flexible schema fit real operational data where structure varied by report type. What I noticed quickly was that stored JSON matched serialized class structures very naturally.

At the same time, search requirements exposed a limitation. CosmosDB queries were not ideal for complex cross-document search in that period, and full-text search was not available. So I deliberately combined CosmosDB with Azure Search, later renamed Azure Cognitive Search. CosmosDB became the primary document registry, and Azure Search became the indexing and cross-document search engine.

That felt different from my previous database work. The data shape was now being expressed directly by program structure. It was the first time I had worked with a database model where stored structure and program structure aligned this directly.

Impact of JSON document storage

In this model, I no longer started by defining schema in an external artifact and then mapping code to it. Data format emerged from application-side classes.

Compared with traditional RDB workflows, this felt unexpectedly refreshing. It reduced both the psychological and structural distance between what the program was and what the database stored. That realization later prepared the ground for adopting Entity Framework.

Entity Framework already existed at that time. I simply had not encountered it in my own projects yet.

Realization: data and code are inseparable

The main insight was simple. Data design and program design cannot be separated in practice.

If those definitions diverge, the application eventually breaks at runtime or during maintenance. After the CosmosDB experience, the old separation between schema documents and application models started to feel increasingly uncomfortable.

That discomfort changed how I evaluated database tooling.

Adopting Entity Framework with code-first

After that, I moved to Entity Framework with a code-first approach. Entity classes became the primary expression of data structure and system behavior assumptions. With code-first modeling, data design and behavior design happened in the same place, instead of being split across separate artifacts. That removed a conceptual gap between how the system should behave and how data should be shaped. Entity Framework migrations became the synchronization mechanism between code and database.

This approach still requires operational discipline. Migration sequencing, rollout timing, and rollback planning are real concerns.

Even with those risks, the conceptual clarity was worth it for my projects.

Limits of code-first

Code-first does not solve every database concern. Database-specific features, full-text indexing strategies, and specialized performance tuning often still need manual configuration.

So I do not treat EF as total automation. But for core structure definition, keeping the source of truth in code greatly reduces drift.

Practical impact on development

In the last several years, every system I built has used Entity Framework. The operational effect has been consistent. Schema and code stay aligned with less coordination work. Manual SQL writing decreases. Spelling mistakes in handwritten SQL become rare in daily development.

Another practical gain is documentation. Schema documents and metadata can now be generated with tools, and increasingly with AI-assisted workflows, from the same model definitions used in implementation.

Why this matters most for small teams

The biggest advantage appears in solo development or small teams. When one person or a few people handle both application and data concerns, reducing manual synchronization has direct impact on delivery speed.

Less synchronization overhead means less operational friction. It also means fewer subtle mismatch bugs between schema and code.

For small teams, that change is not just convenience. It is a structural productivity multiplier.

Closing reflection

Traditional database-first design is not wrong. It remains valid in many contexts.

But in modern application-driven development, defining data structures directly in code has become a strong architectural advantage in my work.

C# Architecture Notes

This article is part of the Cotomy C# Architecture Notes, which reflect on backend and project-structure decisions around business systems.

Series articles: Why I Chose C# for Business Systems and Still Use It , From Global CSS Chaos to Scoped Isolation , Unifying Data Design and Code with Entity Framework, and How I Split Projects in Razor Pages Systems .

Next article: How I Split Projects in Razor Pages Systems

Learn Cotomy

Cotomy is a DOM-first UI runtime for long-lived business applications.