Register to get access to free programming courses with interactive exercises

Entity Objects, Value Objects, and Embedded Objects JS: Object oriented design

Entities objects

When people talk about OOP, they generally talk about domain entities, such as users, orders, goods, and so on. The use of these objects has certain conditions that must be met to ensure normal functioning.

It's worth saying that using OOP in such a way, although it's described in all textbooks as an example of the necessity of OOP, has little to do with real code. In practice, most of the existing classes and objects in application code, libraries, and frameworks have no connection to the subject area. Their emergence and use revolves around topics such as polymorphism. which is studied in the corresponding course.

Lifetime. Such objects are not created to be used once, but rather they live for some time while the program is running or, more often, between launches, in a repository. For example, users in Hexlet are represented by User class objects. They're created during registration and then exist in the system indefinitely. Occasionally, they're deleted via the user's actions.

Id. How is one user different from another? At first glance, it seems that you can just use your first and last name. But if you think about it, no set of parameters will give 100% reliability, and they can and will change over time. Therefore, when working with entities, it's a good idea to introduce artificial identifiers, which, as a rule, are formed by the database. Then it's just them that does the comparison.

class User {
  constructor(id, name) {
    this.id = id;
    this.name = name;
  }

  equals(user) {
    return this.id === user.id;
  }
}

// From our system's perspective, it's the same user
// from JS's perspective, they're different objects
const user1 = new User(3, 'mike');
const user2 = new User(3, 'mike');

const user3 = new User(1, 'mike');

// A similar check scheme exists in all ORMs
user1.equals(user2); // true
user1.equals(user3); // false

This is very similar to the object comparison mechanism in JavaScript. They're not compared by matching data, but by a link, which is represented internally by some numerical value. So different objects storing the same data are always different objects, which is logical.

Objects that have their own identity and lifetime are called entity objects. But besides them, there's another kind of objects, which are also, as a rule, related to the subject area – these are value objects. What are they?

Value objects

When we have $10 in our wallet, we don't care what kind of bill it is. We could easily take that bill and exchange it for another one of equivalent value. Nothing changes for us at this point. Ten dollars is ten dollars. The same can be said for many other things, such as shipping addresses, country of residence, file path, webpage addresses, points on a plane. In all these situations we need to think about the value itself, the very fact of its existence.

Imagine a system that deals with money. And in different currencies. In this situation, it's convenient to think of money as an object that stores information about the currency in addition to the face value. How should the comparison work in this case?

const m1 = new Money(150, 'usd');
const m2 = new Money(130, 'eur');

// Let's assume that $150 at the current exchange rate equals 130 €
// the function converts money for comparison
m1.equals(m2); // true

This code expresses the idea expressed earlier. We don't care about objects, we care about values. The object here serves only as a way of organizing code, it doesn't identify the data stored within it in any way. Such objects are called value objects.

The reality is a little more complicated, to tell the truth. Things can be both value objects and entity objects at the same time. It all depends on the specific subject area. For most companies, money is just value, but not for those who print it. They need to distinguish bills from each other, and because of it, each note has a unique number on it, which allows for identification.

Value objects are artificial things. Often they're not needed, and it's enough to use a simple value, especially if it is primitive. On the other hand, when a value is composite, such as a point on a plane, an address, or a URL (it consists of many parts), such objects help simplify the code through convenient abstraction. JavaScript has a built-in URL class used to represent a web address as an object with a lot of convenient methods:

import { URL } from 'url';

const url = new URL('/courses?page=2', 'https://hexlet.io');
url.host; // 'hexlet.io'
url.pathname; // '/courses'
url.searchParams.get('page'); // '2'

In theory, two addresses are equal if they're the same address. Unfortunately, JavaScript doesn't present an easy way to check equality, so there are quite a few articles on the web in which the authors try to solve this problem for themselves. This situation can most likely be attributed to a bug (flaw) in the design of the standard library since a URL is a typical example of a value object.

Embedded objects

Typically, the data that web applications work with is stored in relational databases. In them, each entity is represented by a row in a table, where each field corresponds to a property of the object. With this kind of storage organization, there are sometimes situations where multiple entity properties describe one thing. A common example is a mailing address:

// Search from a database by identifier
// Hypothetical code
const user = User.find(5);

user.street; // 'Main Street'
user.zipcode; // 10044
user.house; // 10

There are two approaches for dealing with such data. According to the first approach, any logic for dealing with this data is described within the entity itself. For example, outputting the address as text:

class User {
  // Somewhere in here is a constructor and other methods

  getFullAddress() {
    return `${this.street}, ${this.house}, ${this.zipcode}`;
  }
}

user.getFullAddress();

The main problem here is possible duplication if the address occurs somewhere else than the user. Then you have to implement methods to work with this data everywhere they occur.

The second approach is to create a separate class and embed an object of that class in the main object. It sounds scary, but in practice it's very simple:

class Address {
  constructor(street, house, zipcode) {
    this.street = street;
    this.house = house;
    this.zipcode = zipcode;
  }

  toString() {
    return `${this.street}, ${this.house}, ${this.zipcode}`;
  }
}

class User {
  // Somewhere in here is a constructor and other methods

  getAddress() {
    // Since we have an object-value,
    // you can create it any number of times without fear,
    // but this process can be optimized if necessary
    return new Address(this.street, this.house, this.zipcode);
  }
}

user.getAddress().toString();

Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

For full access to the course you need a professional subscription.

A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

Bookmate
Health Samurai
Dualboot
ABBYY
Suggested learning programs
profession
Development of front-end components for web applications
10 months
from scratch
Start at any time

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.