CodamAIDocs
Topicdone

Both sides of a relation

CDMS maintains the other side of a relation automatically. Why the client should not send the back reference.

Variants
1:11:n from the 1 side1:n from the n siden:mBack reference sent alongPUT without the list of the other side

What this is about

In CDMS, a relation has two ends: company on the employee and employees on the company. Both describe the same connection. The field on the other side is called the other side or back reference.

flowchart LR
    E["Employee Anna"] -->|"company"| C["Company Codamic"]
    C -->|"employees"| E

CDMS maintains both ends

As soon as CDMS connects two objects, it sets both fields:

Typeyou setCDMS also sets
1:1person.mainPhone = P1P1.person = person
1:ncompany.employees = [Anna]Anna.company = company
n:1anna.company = Cadds Anna to C.employees
n:mgroup.roles = [R1]adds the group to R1.groups

The same happens when you remove a connection: if you remove Anna from employees, Anna.company becomes empty. So you can write every relation from the side that is more convenient for your use case. When you read, you see the connection on both sides.

Which side to write from?

The same relation, two directions

When: You write the company with its list employees.

  1. 1
    Client→CDMS
    sends PATCH /company/update/{id} with "employees": [{ "id": "anna…" }, { "id": "ben…" }]
  2. 2
    CDMS
    sets company on Anna and Ben to this company
  3. 3
    CDMS
    removes all other employees from the list, because the list is the target state

Result: The company has exactly Anna and Ben. See Lists as target state.

When: You write an employee with their company.

  1. 1
    Client→CDMS
    sends POST /employee/create with "company": { "id": "c1…" }
  2. 2
    CDMS→Database
    looks up the company c1…
  3. 3
    CDMS
    sets company and adds the employee to the company's employees

Result: The other employees of the company stay untouched. For single assignments, this is the simpler way.

When: A person and their main phone.

Both sides work the same: person.mainPhone = { "id": … } or phone.person = { "id": … }. CDMS sets the other end in each case. To replace a child, you first remove the old one (field to null) and then set the new one. The easiest way is two steps.

Result: Exactly one connection between the two objects.

When: Groups and roles.

You can write group.roles or role.groups. Both ways create the same connection. Both are a target state for the list you send. The list on the other side only changes as far as this one connection is concerned.

Result: See Many-to-many through a join table.

Send the back reference along?

When you send back objects you have read, the back reference easily ends up in the request, for example company in every employee of the list employees:

Back reference in the child
Request
PUT /api/rest/company/update/c1…
{
  "data": {
    "id": "c1…",
    "companyname": "Codamic AG",
    "employees": [
      { "id": "anna…", "firstname": "Anna", "lastname": "Schmidt",
        "company": { "id": "c9…" } }
    ]
  },
  "response": ["+"]
}
What happens
Afterwards Anna belongs to c1…, not to c9….
The company you write from wins.
There is no error.
Back reference in the child
Back reference in the childpoints toResult
missing–CDMS sets it to the parent object
presentthe parent objectsame as above, duplicated but harmless
presenta different objectis overwritten, the parent object wins
presentan unknown id404, the request fails

So do not send the back reference. It adds nothing and can only go wrong.

PUT and the list of the other side

PUT describes the whole object, including its lists. This is true for every side of a relation. If you write a role with PUT and leave out its list groups, CDMS removes all connections of the role to groups.

Change a role without losing its groups
PUT
whole object
  • first read the role with groups
  • send groups back with all ids
  • otherwise all connections are gone
PATCH
only what changed
  • send only the changed fields
  • leave out groups
  • the connections stay untouched

Pitfalls

What comes next

Sources in the code and the knowledge base
  • CDMS/cdms-system-layer – AbstractLayer.setReference, removeBackReference, recursiveUpdate (excludeField)
  • hub-backend – ModelDesignService (mirror field of the other side)
  • CDMS/frontend – server/utils/cdmsInverseCollections.ts
  • CDMS/cdms-integrationtest – AbstractRecursiveTest (createEmployeeWithExistingDepartment_Success), AbstractManyToManyTest, AbstractUpdateTest
Search