DEV Community

Cover image for đź§Ľ Data Hygiene: The Real Foundation of Angular Front-End Architecture
Florent M
Florent M

Posted on

đź§Ľ Data Hygiene: The Real Foundation of Angular Front-End Architecture

Structure your data like a DBMS (e.g., PostgreSQL, MySQL...) to ensure the scalability of your Angular apps.


I'm the co-founder and CTO of Disign, a SaaS software for digital signage that transforms any screen into a powerful visual communication tool. Whether you're using cloud-based digital signage, deploying content on Android digital signage players (including Android TV), or leveraging Raspberry Pi digital signage through our custom embedded operating system DisignOS with OTA updates and system-level management, we support a wide range of devices and configurations.

We manage several complex applications built with Angular—including Disign Studio, our content creation and management interface, as well as embedded technologies powering our player apps across platforms.

Today, I want to talk about a more fundamental—and often overlooked—topic: data hygiene. In my opinion, it's the bedrock of a solid, maintainable, and scalable front-end project.
We manage several complex applications built with Angular, including Disign Studio, our content creation and management interface.


Let's start with the most important: Your Data

If there's one concept you should take away from this article, it's this one. An application can be performant, tested, well-coded... but if the data structure is flawed, everything else eventually crumbles.

Let's take Disign Studio as an example. Our content management and creation platform allows you to:

  • manage screens worldwide, across multiple sites and locations,
  • create beautiful content with full customization, whether no-code or low-code,
  • build interactive touch applications to go beyond passive signage,
  • integrate live data, enable real-time sync, and trigger automated content flows,
  • schedule complex rotations, set rules, or respond to specific events,
  • control players remotely: turn screens on/off, preview content in real time, track performance,
  • and support any type of hardware, our lightweight Disign OS runs on Raspberry Pi, Android and Android TV, and other digital signage players like Tizen or BrightSign.
  • and offer all the essential features for professional digital signage.

A few figures to set the scene:

  • 2000+ Angular components (a large-scale app)
  • 175 business entities on the front end (even more on the back end)

Here, entities refer to the "models" representing business objects — the equivalent of a SQL table — not Angular features, states, or components.

And the reason it all works? The data is well-structured from the start.


Design your data like a DBMS

Too often, I see developers ignoring the basics of relational modeling in the frontend. But thinking of your entities like SQL tables is an excellent way to guarantee clarity and scalability: Smart people have spent decades figuring out the best ways to structure data. They didn't wait for you, or for Angular, to propose solutions to these problems.

Basic principles to follow:

âś… Data uniqueness: no unnecessary duplication.
âś… Explicit relationships: 1-n, n-n, via IDs.
âś… No computed data: keep that for runtime or views.
âś… Flat design: avoid deeply nested JSON structures.
âś… Scalars only: store simple values (no JSON, no Date objects).
âś… If you must store a JSON: treat it as a "string" (JSON.stringify), like a basic DB would (deliberately ignoring JSONB or other optimizations).

Example typescript entities:

export interface Screen {
  id: string;
  name: string;
  campaignId: string; // 1-n relation
}

export interface Campaign {
  id: string;
  name: string;
}
Enter fullscreen mode Exit fullscreen mode

Instead of nesting screens inside a Campaign, you create two normalized entities, indexed by ID-just like in a relational database.

What to avoid at all costs

  • Nested objects (campaigns in screens and vice versa)
  • Duplicate data
  • Partially represented entities (incomplete in some contexts)

This leads to technical debt, UI bugs, complex state, and maintenance headaches...

Don't store UI states (modals, scroll positions, etc.) in your business entities. You can of course create state entities for UI, but mentally separate them from business entities.


Why this rigor is essential?

Because everything else in your app stems from it:

  • State management becomes simpler
  • Data handling in components is intuitive
  • Maintenance and evolution are easier
  • Migrations can be done without breaking structure

And most importantly: if your data is clean, you almost never need ugly code or weird hacks in your components.

Data is the only source of truth. If it's wrong, your UI will be too.


REST API: Built for the app, not the public

Understand this crucial point: the REST API of your app is not public, nor generic. There's no point in creating a fully REST-compliant API with URLs like:

/screens/{screen_id}/campaigns/{campaign_id}

It's pointless. In most cases, a valid URL only needs one required parameter.
If your data is correct on the backend, that's enough to:

  • retrieve everything,
  • manage security (permissions, roles, etc.)...

Of course, there are many cases where more required parameters are needed, such as managing different contexts.

So, to sum up: design your API for your application.

If you need another API, say, a public one, build a separate API.

That doesn't mean duplicating code (hopefully all your business logic is easily reusable and independent of views).

It's built exclusively to serve our Angular application.

And so: it's optimized to be easily consumed by our state management, using the same data models as defined on the frontend.

Just a quick note on GraphQL: I really don’t think it’s well-suited for this kind of use case. It tends to overcomplicate things where a clean, app-specific REST API does the job far more effectively.

A well-designed REST API is... flat

Typical API response format at Disign (simplified):

{
  "pagination": {
    "total": 100,
    "page": 1,
    "limit": 25,
    "ids": []
  },
  "entities": {
    "campaigns": {
      "id1": { "id": "id1", "name": "Campaign 1" },
      "id2": { "id": "id2", "name": "Campaign 2" }
    },
    "screens": {
      "s1": { "id": "s1", "name": "Screen 1", "campaignId": "id1" },
      "s2": { "id": "s2", "name": "Screen 2", "campaignId": "id1" }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Why this format?

  • It mirrors exactly our ngrx/entity structure.
  • It populates the store without intermediate processing.
  • It adheres to all principles of data hygiene.

It's not about being “pretty.” This format avoids:

  • unnecessary selectors
  • cascading side effects
  • ambiguous updates

Of course, it can be adapted to fit your needs-but you get the idea.

The front-end == An in-memory database

What you need to understand is that state management data is nothing more than an in-RAM database. I'm talking here about business data and models, not UI state or others.

At Disign, we use NgRx with its entity module, which structures our entities like this:

export interface EntityState<T> {
  ids: string[];
  entities: { [id: string]: T };
}
Enter fullscreen mode Exit fullscreen mode

And our API returns data in exactly this shape. The result:

  • No unnecessary mapping
  • Simple and effective selectors
  • Guaranteed scalability

The architecture becomes intuitive: changing an entity in the state = immediate, consistent UI updates.

State management: A necessary evil?

NgRx is often criticized: too verbose, hard to learn, not suited to every use case.
And that's true. If you have a simple app, you probably don't need it.

But at Disign, with 175 front-end entities, it's not even a question-we need it. Again, these designs weren't made to please people but to solve real problems. Nothing is perfect, but it gives us a logic to follow and makes teamwork easier.

At Disign, we use NgRx-we started with Angular 2 back in the day (2016?), there was no CLI, and NgRx was one of the first libraries available. We like it despite its flaws. We work around them with a custom layer we built for digital signage CMS Disign.

We developed:

  • meta-reducers: inter-entity communication
  • an automated migration system from reducer to createReducer
  • internal conventions to standardize all actions, effects, and selectors

It's a lot of effort. But it's also what allows us to:

  • maintain a healthy codebase
  • standardize our code
  • avoid long-term technical debt

And as always: the end user doesn't care. But it's vital for developers.

Are we just repeating the obvious?

Yes, maybe. But I assure you: in practice, many developers skip these fundamentals. The result: unnecessarily complex code, painful migrations, and slow feature delivery.

And that's not a matter of framework, Angular version, or RxJS magic. It's just data architecture.

Conclusion

We often underestimate the fundamental impact of data structure in a front-end application. Yet it's the starting point for everything else.

At Disign, if our applications are maintainable and scalable despite their complexity, it's largely thanks to this data discipline.

"Good data design means simple code. Bad data means complex code for no reason."

Never forget: your front end is just a RAM database. It's up to you to keep it clean.

And You?

Working on a large Angular app? Ever had to rebuild a data architecture after structural chaos? I'd love to hear your experiences.

Top comments (0)