# Architecture
Understand dotlottie-io's four core classes — DotLottie, DotLottieBuilder, DotLottieReader, and DotLottieMerger — and when to use each.

# Architecture

`dotlottie-io` splits responsibilities across four classes. Each one is optimized for a different task.

| Class                                                                      | Role                                              | Use it when...                                                                |
| -------------------------------------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------- |
| [`DotLottie`](/docs/tools/dotlottie-io/api/dotlottie-class)                | A loaded, fully mutable package                   | You want to load a file and then read _and_ modify it                         |
| [`DotLottieBuilder`](/docs/tools/dotlottie-io/api/dotlottie-builder-class) | A stateful builder for constructing a new package | You're assembling a package from scratch                                      |
| [`DotLottieReader`](/docs/tools/dotlottie-io/api/dotlottie-reader-class)   | A lazy, read-only view over an archive            | You only need to inspect or extract specific entries, with minimal memory use |
| [`DotLottieMerger`](/docs/tools/dotlottie-io/api/dotlottie-merger-class)   | Combines multiple packages                        | You need to merge two or more `.lottie` files with defined collision handling |

## Why the split

Each class trades off differently between mutability, memory use, and construction cost:

- **`DotLottie`** eagerly parses every entry in the archive into memory, so once loaded, all reads and writes are immediate and synchronous. This is the right default for small-to-medium packages you intend to modify.
- **`DotLottieBuilder`** exists because building up a package incrementally (adding animations, themes, assets one at a time) is a distinct workflow from mutating an already-loaded one — `build()` consumes the builder's queued content and resolves any filename collisions in one pass, returning a `DotLottie`.
- **`DotLottieReader`** only parses `manifest.json` on construction. Every other getter reads exactly one ZIP entry on demand, so inspecting a single animation out of a 200-animation library doesn't require loading the other 199. See [Managing Assets](/docs/tools/dotlottie-io/core-concepts/assets) and the [reading guide](/docs/tools/dotlottie-io/guides/reading-a-lottie-file) for when this matters in practice.
- **`DotLottieMerger`** is separate from both because merging needs a single place to define collision behavior ([`MergeStrategy`](/docs/tools/dotlottie-io/api/dotlottie-merger-class#mergestrategy)) that applies consistently across every animation, theme, state machine, and asset being combined.

## A typical flow

```javascript
const { DotLottieBuilder, DotLottieMerger, MergeStrategy } = require("@lottiefiles/dotlottie-io");

// 1. Build two packages
const builder1 = new DotLottieBuilder();
builder1.addAnimation("intro", introBuffer);
const packageA = builder1.build(); // → DotLottie

const builder2 = new DotLottieBuilder();
builder2.addAnimation("outro", outroBuffer);
const packageB = builder2.build(); // → DotLottie

// 2. Merge them
const merger = new DotLottieMerger(MergeStrategy.Rename);
const merged = merger.merge(packageA, [packageB]); // → DotLottie

// 3. Serialize the result
merged.toBytes();
```

Reading later reuses the same `DotLottie` class:

```javascript
const { DotLottie } = require("@lottiefiles/dotlottie-io");

const loaded = DotLottie.fromBytes(mergedBytes);
loaded.animationIds(); // ['intro', 'outro']
```

Next up: [Managing Animations](/docs/tools/dotlottie-io/core-concepts/animations)
