The DTO Sync Tax
Every full-stack .NET developer knows this pain:
"One thing that always annoyed me in full-stack .NET + TypeScript projects is keeping C# DTOs/ViewModels in sync with TS interfaces."
You change a C# model. You forget to update the TypeScript interface. The frontend breaks silently. Or you spend hours setting up code generation tools that add yet another build step.
I saw a developer share a tool for generating TypeScript interfaces from C# assemblies. It handles camelCase, nullable types, basic type mapping. Clever solution.
But here's a question: what if you didn't need TypeScript interfaces at all?
The Real Problem
DTO sync is a symptom, not the disease. The disease is having two codebases that need to agree on data shapes.
When you build internal tools - dashboards, admin panels, back-office apps - you're not building a public API that multiple clients consume. You're building one app. For your team.
So why maintain two languages?
The Ivy Approach: No Frontend
Ivy takes a different path. Instead of syncing types between C# and TypeScript, you write everything in C#:
[App]
public class EmployeeAdmin : ViewBase
{
public override object? Build()
{
var db = UseService<AppDbContext>();
return Layout.Vertical()
| Text.H1("Employees")
| db.Employees.ToDataTable(e => e.Id)
.Header(e => e.Name, "Name")
.Header(e => e.Email, "Email")
.Header(e => e.Department, "Department")
.Config(c => { c.AllowSorting = true; c.AllowFiltering = true; });
}
}
That's it. No TypeScript. No interface generation. No sync issues. The Employee type is defined once in your C# codebase and used directly in the UI.
How This Works
Ivy renders your UI server-side. The browser receives widgets, not business logic. State lives on the server - which means:
- One source of truth - Your C# models are the only models
- Compile-time safety - Change a property, get compile errors if the UI references it wrong
- No build pipeline - No webpack, no npm, no TypeScript compiler
- Direct database access - Query your DbContext right in the UI component
Compare this to the generation approach: you still have two representations of your data. You still need a build step. You still need to remember to regenerate when models change.
When to Use What
DTO generation tools still make sense if you're building a public API, supporting multiple clients (mobile, web, third-party), or your frontend team prefers TypeScript.
But for internal tools? Consider skipping TypeScript entirely:
- Your team is already C# developers
- You want to ship fast without frontend coordination
- Compile-time safety matters more than runtime flexibility
The Trade-off
Ivy's approach trades flexibility for simplicity. You can't build a mobile app this way. You can't have third parties consume your "API" (there is no API - the UI is the interface).
But for internal tools? That trade-off is often worth it. You get a single codebase, single language, single deployment. No sync issues because there's nothing to sync.
Try It
If you're tired of the TypeScript tax on internal tools, check out Ivy on GitHub. You might find that the best TypeScript interface is no TypeScript interface.
What's your take - is DTO generation worth the complexity for internal tools, or would you rather eliminate the frontend entirely?
