I have an API which denormalizes data causing circular dependency. Is there a way to refactor the following using abstract classes, interfaces, composition or other techniques where I wouldn't need to create N partial classes for each entity to avoid a circular dependency in my Angular application's type definitions?
model.ts
export abstract class Model {
// ... Model related data members and functions
}
person.ts
import { Model } from './model';
import { Site } from './site';
export class Person extends Model {
// ... Data
site: Site; // Partially or Fully saturated site entity
}
site.ts
import { Model } from './model';
import { Person } from './person';
export class Site extends Model {
// ... Data
people: Person[]; // array of partially saturated people entities, site is left undefined
}
I would like to maintain a single definition for each model, instead of redefining Site
, Person
, etc each time there is a dependency like this.
This problem is caused by a bad class model design and it does not depend on the language. I hope this helps :)
The circular dependency problems generally include the chicken & egg problem, because when you want to instantiate an object you do not know which you should instantiate first.
This problem can be easily solved by only referencing objects by their identities, and instead of having a direct dependency on a big object as a part of constructor. For example, modifying Person class:
import { Model } from './model';
export class Person extends Model {
// ... Data
siteId: number;
}
If you need the full object inside Person, I think a good approach would be to change the inheritance tree.
I understand that your data model is about a website, its users and its owner. Well, lets break this down: make Person
an abstract class and extend it with User
and Owner
classes.
import { Model } from './model';
export abstract class Person extends Model {
// ... Data
// Does not include the Site attribute!
}
Users
will be the "readers" of the Site
, but don't need a reference to it.
import { Person } from './person';
export class User extends Person {
// ... Data
}
Owner
will be the creator of the Site
. This is the class that has the reference to the Site
.
import { Person } from './person';
export class Owner extends Person {
// ... Data
site: Site;
}
Finally, the Site
keeps track of its users.
import { Model } from './model';
import { User } from './user';
export class Site extends Model {
// ... Data
users: User[];
}
I think this related article could be of help for understanding circular dependencies: How to fix nasty circular dependency issues once and for all in JavaScript & TypeScript
Why don't use internal module pattern which export all the class and used internally by models to load dependencies.
Example Application: https://codesandbox.io/s/circular-deps-fix-using-internal-module-pattern-mtfro
internal.ts
export * from './model'
export * from './person'
export * from './site'
model.ts
export abstract class Model {
// ... Model related data members and functions
}
person.ts
import { Model, Site } from './internal';
export class Person extends Model {
// ... Data
site: Site; // Partially or Fully saturated site entity
}
site.ts
import { Model, Person } from './internal';
export class Site extends Model {
// ... Data
people: Person[]; // array of partially saturated people entities, site is left undefined
}
the above rules only apply to our local dependencies. External module imports are left as is. They are not involved in our circular dependency problems after all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With