Skip to content

You Don't Need Domains

Domain in Effector is a namespace for Events, Effects and Stores. It could be used for two purposes:

  1. Semantic grouping of units
  2. Bulk operations on units

However, in most cases, you do not need Domains at all. Let us see why.

Semantic Grouping

JavaScript does have semantic grouping of entities: it is modules. Since you do not have an option not to use modules, you will be using them to group your units anyway. So, why do you need another grouping mechanism?

ts
// 👇 all units are already grouped by module
// src/features/counter.ts

import { createEvent, createStore, sample } from 'effector';

export const increment = createEvent();
export const decrement = createEvent();

export const $counter = createStore(0);

sample({
  source: $counter,
  clock: increment,
  fn: (counter) => counter + 1,
  target: $counter,
});

sample({
  source: $counter,
  clock: decrement,
  fn: (counter) => counter - 1,
  target: $counter,
});
ts
// 👇 all units are already grouped by module
// src/features/counter.ts

import { createDomain, createEvent, createStore, sample } from 'effector';

// AND by domain, so it is redundant
const counterDomain = createDomain();

export const increment = createEvent({ domain: counterDomain });
export const decrement = createEvent({ domain: counterDomain });

export const $counter = createStore(0, { domain: counterDomain });

sample({
  source: $counter,
  clock: increment,
  fn: (counter) => counter + 1,
  target: $counter,
});

sample({
  source: $counter,
  clock: decrement,
  fn: (counter) => counter - 1,
  target: $counter,
});

Bulk Operations

But Domains are not only about grouping. They also allow you to perform bulk operations on units.

For example, you can reset values of all Stores in the Domain with the following code:

ts
import { createDomain, createStore, createEvent } from 'effector';

const domain = createDomain();

export const someEvent = createEvent({ domain });

export const $store1 = createStore(0, { domain });
export const $store2 = createStore(0, { domain });
export const $store3 = createStore(0, { domain });

// 👇 callback will be called on every Store in the Domain
domain.onCreateStore((store) => {
  store.reset(someEvent);
});

This approach has a significant drawback: it is implicit. In case of creating a new Store in the Domain, you will have to remember that trigger of someEvent will reset the new Store as well. It is really easy to forget about it.

Things become even worse if you have more than one bulk operations in the Domain.

Instead of using Domains, you can explicitly perform bulk operations on units. The previous example can be rewritten as follows:

ts
import { createDomain, createStore, createEvent } from 'effector';

const domain = createDomain(); 

export const someEvent = createEvent({
  domain, 
});

export const $store1 = createStore(0, {
  domain, 
});
export const $store2 = createStore(0, {
  domain, 
});
export const $store3 = createStore(0, {
  domain, 
});

// 👇 callback will be called on every Store in the Domain
domain.onCreateStore((store) => {
  store.reset(someEvent); 
});

// 👇 now it is explicit
resetMany({ stores: [$store1, $store2, $store3], reset: someEvent }); 

function resetMany({ stores, reset }) {
  for (const unit of stores) {
    unit.reset(reset);
  }
}

This approach not only more explicit but also less verbose, because you do not need to specify Domain for every unit.

Summary

  • Do not use Domains for semantic grouping - use modules instead
  • Do not use Domains for bulk operations - use explicit functions instead

Released under the MIT License.