Design Patterns in Odoo: Proxy

If you work with JavaScript code in Odoo, you might come across Proxy. It is a structural design pattern that provides a substitute for another object. A proxy controls access to the original object, allowing additional actions either before or after the request reaches the original. Think of it like a security guard at a building entrance: the guard checks credentials before allowing access.

Overview of the Pattern

Sometimes, extra logic is required when accessing an object. This might be for delaying initialization, restricting access, caching results, or handling other concerns. Directly modifying the original object is not always ideal, because it can complicate the code and violate the single responsibility principle.

The Proxy pattern solves this by introducing a new proxy object with the same interface as the original. Clients interact with the proxy, unaware that it isn’t the real object. The proxy then manages access to the original, adding logic before or after delegating the request.

How to Implement

JavaScript provides a built-in Proxy object for implementing this pattern. It requires two arguments:

  • Target object: the original object.

  • Handler object: defines the custom behavior (traps for get, set, etc.).        

Example 1: Access Control in module localization

export const localization = new Proxy(
  {},
  {
  get: (target, p) => {
  if (p in target || p === "then") {
  return Reflect.get(target, p);
  }
  throw new Error(
  `could not access localization parameter "${p}": parameters are not ready yet...`
  );
  },
  }
);

Here, localization is a proxy object that holds user-specific localization data. Its properties are populated later in localizationService:

Object.assign(localization, {
  dateFormat,
  timeFormat,
  shortTimeFormat,
  dateTimeFormat,
  decimalPoint: userLocalization.decimal_point,
  direction: userLocalization.direction,
  grouping,
  multiLang,
  thousandsSep: userLocalization.thousands_sep,
  weekStart: userLocalization.week_start,
  code: jsToPyLocale(locale),
});

When accessing a property (e.g., localization.dateFormat), the proxy intercepts the call. If the property does not exist, it throws an error to prevent silent bugs caused by uninitialized data. 

Using built-in Reflect eases the target object manipulation

Example 2: Reactivity and Lazy Computation in module mail 

get(record, name, recordFullProxy) {
  // ...
  // Logic for lazy computation
  if (Model._.fieldsCompute.get(name) && !Model._.fieldsEager.get(name)) {
  record._.fieldsComputeInNeed.set(name, true);
  if (record._.fieldsComputeOnNeed.get(name)) {
  record._.compute(record, name); // Computes the value on-demand
  }
  }
  // ...
  return Reflect.get(record, name, recordFullProxy);
  },

The get intercepts property access. If the computed field is not eager, it marks it for computation and calculates its value only when needed. This avoids unnecessary work, improving performance.

set(record, name, val, receiver) {
  // ...
  // batching logic
  return store.MAKE_UPDATE(function recordSet() {
  store._.updateFields(record, { [name]: val });
  // ...
  return true;
  });
  },

});

The interesting part is the set trap. Instead of directly assigning, it runs store.MAKE_UPDATE, which batches multiple changes into a single update cycle, ensuring that the UI reacts efficiently to data modifications.

Pros and Cons

Pros:

  • Advanced functionality: validation, security checks, caching, etc., without altering the original code.

  • Lazy initialization: expensive objects are created only when needed.

  • Separation of concerns: keeps add-on features distinct from core business logic.

Cons:

  • Increased complexity: an additional layer can make debugging harder.

  • Performance overhead: every operation is intercepted, which can introduce a small performance penalty.

Conclusion

The Proxy pattern in Odoo’s JavaScript code provides a powerful way to control and extend object behavior without modifying the object itself. Whether for lazy computation, validation, or structured data handling (like localization), proxies help enforce robust and maintainable code. 

Reference
Proxy pattern. Patterns.dev. (n.d.). https://www.patterns.dev/vanilla/proxy-pattern/