sequelize-typescript icon indicating copy to clipboard operation
sequelize-typescript copied to clipboard

How are people dealing with circular dependencies?

Open theogravity opened this issue 3 years ago • 7 comments

I have models define in separate files that reference each other. Is there a way to deal with circular dep issues without combining the models into one file?

import { Table, Column, Model, HasOne } from 'sequelize-typescript';
import User from './User.model';

@Table({ timestamps: true })
export default class Post extends Model {
  @HasOne(() => User)
  author!: User;

  @Column
  title!: string;

  @Column
  content!: string;
}
import { Table, Column, Model, HasMany } from 'sequelize-typescript';
import Post from './Post.model';

@Table({ timestamps: true })
export default class User extends Model {
  @Column
  name!: string;

  @HasMany(() => Post)
  posts!: Post[];
}

theogravity avatar Sep 07 '21 00:09 theogravity

I am also fighting with this, and my "solution" up until now has been to disable the linter rule, and so far, everything has been happy. I'm guessing, though, that I've just been lucky and it'll bite me sooner or later. I would also love to know what people are doing to solve this problem, as I certainly don't want to put all my models into a single gigantic file.

geoffoliver avatar Sep 16 '21 04:09 geoffoliver

There's really no way around it, that's why they're using arrow functions to defer the model resolution in associations. As long as you don't start importing other unnecessary stuff to your entity files, you should be alright.

ghost avatar Sep 22 '21 15:09 ghost

Adding to this thread as I was facing the same problem. Is just ignoring the linter error the best way of dealing with this?

Tahmeed27 avatar Oct 25 '21 00:10 Tahmeed27

There's really no way around it, that's why they're using arrow functions to defer the model resolution in associations. As long as you don't start importing other unnecessary stuff to your entity files, you should be alright.

No, it's not alright. It prevents models from being imported when using dynamic import. We were somewhat alright using webpack 4 require (I'm assuming out reload problems were still caused by it), but in webpack 5, it actually doesn't work anymore.

The correct approach would be for the e.g. @BelongsTo association to receive the models objects with statically initialized models in order to do post initialization association.

mschipperheyn avatar Jan 25 '22 12:01 mschipperheyn

IMHO the code below should be adjusted to help avoid the need for recursive imports:

private associateModels(models: ModelCtor[]): void {
    models.forEach((model) => {
      const associations = getAssociations(model.prototype);

      if (!associations) return;

      associations.forEach((association) => {
        const options = association.getSequelizeOptions(model, this);
        // const associatedClass = this.model(association.getAssociatedClass());
        
        // should become
        const associatedClass = this.model(association.getAssociatedClass(models));
        
        if (!associatedClass.isInitialized) {
          throw new ModelNotInitializedError(
            associatedClass,
            `Association between ${associatedClass.name} and ${model.name} cannot be resolved.`
          );
        }
        model[association.getAssociation() as any](associatedClass, options as any);
      });
    });
  }

So that

import Company from './Company';
[...]
@BelongsTo(() => Company, {
    foreignKey: 'companyId',
    as: 'company',
})
public company!: Company;

becomes

@BelongsTo((models) => models.Company, {
    foreignKey: 'companyId',
    as: 'company',
})
public company!: Company;

mschipperheyn avatar Jan 25 '22 19:01 mschipperheyn

@RobinBuschmann I took an initial stab at this. Would be great if someone could give me some feedback on whether this is a good idea or not: https://github.com/mschipperheyn/sequelize-typescript/commit/89bbef67201394d2fe4d832a8e2e8c4565e67bb7

mschipperheyn avatar Jan 25 '22 21:01 mschipperheyn

I've added a PR though. It does solve circular depencencies as being thrown by the webpack CircularDependencyPlugin https://github.com/RobinBuschmann/sequelize-typescript/pull/1206

mschipperheyn avatar Feb 11 '22 20:02 mschipperheyn