Strider
Strider

Reputation: 3749

Avoid Circular Dependency injection

In my Angular application, I am implementing 2 services:

These two services depend on each other:

  1. When I logout I have to stop watching the user idleness (by calling a method in the IdleService)
  2. When the user is timed out, I have to call a logout method from the AuthenticationService to log out the user.

This approach is causing a circular dependency problem. Do you have any idea how to avoid it or how to change my actual approach by a better one?

Upvotes: 2

Views: 1256

Answers (3)

LLai
LLai

Reputation: 13406

I would say the auth service is responsible holding, managing, and exposing the state of user authentication. By this I am saying the auth service should not care about user idleness at all. This is the responsibility of the idle service.

So I would create a BehaviorSubject in the auth service that broadcasts the state of the user authentication. The idle service can use the subject to determine when an idle timer needs to start/stop.

export class AuthService {
    user: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

    logIn(){
        // logic to log user in
        this.user.next(true);
    }
    logOut(){
        // logic to log user out
        this.user.next(false);
    }
}

Now the idle service can inject the auth service and determine how to handle the idle timer

export class IdleService {
    constructor(private authService: AuthService){
        this.authService.user.subscribe(res => {
            // watch user authentication state and determine how to handle idle timer
        });
    }

    idleOut(){
        this.authService.logOut();
    }
}

For this to work you will have to inject the IdleService on app load (for example in the app.component so the IdleService constructor method runs. I am not sure if this is the most elegant solution, but this is the first thing that comes to mind. Here is a very basic stackblitz demoing this concept.

Upvotes: 1

Steven
Steven

Reputation: 172666

In the book Dependency Injection in .NET 2nd edition (§6.3), Mark Seemann and I describe that Dependency cycles are often caused by Single Responsibility Principle violations. My impression is that this is the case as well in your specific situation. SRP is typically violated when a class has many methods that are not very cohesive.

The solution is to split up either one of the two classes AuthenticationService and IdleService (or perhaps even both) into smaller classes.

Upvotes: 2

Pedro Arantes
Pedro Arantes

Reputation: 5379

You could create an observable or a subject in IdleService which returns a disconnect message if the times goes out. This way, only AuthenticationService imports IdleService. For example:

import { IdleService } from './services/idle.service';
import { Observable } from 'rxjs/Observable'; 

@Injectable()
export class AuthenticationService {

  disconnect: Obervable<boolean>;

  constructor(private idleService: IdleService) { 
    this.disconnect = this.idleService.disconnectObservable;
    this.disconnect.subscribe(res => {
      if (res === true) {
        this.logout();
      }
    });
  }

  logout() {
  //some code here
  }
}

In the IdleService:

import { Observable } from 'rxjs/Observable';

@Injectable()
export class IdleService {

  public disconnectObservable: Observable<boolean>;

  constructor() {
    this.disconnectObservable = new Observable(observer => {
      // insert your logic here
      setTimeout(() => {
          observer.next('true');
      }, 1000);

      setTimeout(() => {
          observer.complete();
      }, 3000);
    });
  }
}

Upvotes: 0

Related Questions