Tight coupling occurs when a group of classes is highly dependent on each other Loose coupling involves detaching the classes that integrate with the whole.
This WebAPI has a PersonController. The goal is to create a list of Persons.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
I created an interface for a PersonService whose goal was to return Persons.
public interface IPersonService
{
List<Person> GetPersons();
}
A class implementing IPersonService can return the Persons in many ways. For example, it can query a database. In this case, I used a very simple implementation of IPersonService
public class PersonService : IPersonService
{
public List<Person> GetPersons()
{
List<Person> persons = new List<Person>();
persons.Add(new Person
{
Id=1,
Name = "Herminia Mayert",
Address = "3 Rafaela Villages",
City= "3 Rafaela Villages",
Country = "Ghana"
});
persons.Add(new Person
{
Id=2,
Name = "Imogene Skiles",
Address = "4488 Little Inlet",
City= "4488 Little Inlet",
Country = " Guadeloupe"
});
...
return persons;
}
}
Here is the relevant part of the Person Controller.
[ApiController]
[Route("[controller]")]
public class PersonController : ControllerBase
{
private readonly ILogger<PersonController> _logger;
private readonly IPersonService _service;
public PersonController(ILogger<PersonController> logger, IPersonService service)
{
_logger = logger;
_service = service;
}
[HttpGet]
public IEnumerable<Person> Get()
{
_logger.LogInformation("Person API: GetPersons called");
return _service.GetPersons();
}
...
}
I included an ILogger to how it supports loose coupling.
In Program.cs, I have PersonService as the implementation of IPersonService.
builder.Services.AddScoped<IPersonService, PersonService>();
I included two implementations of ILogger in Program.cs.
builder.Logging.AddConsole();
builder.Logging.AddCustomLogger();
CustomLogger is a database logger that I wrote. Both the Console and CustomLogger will log.
Loose coupling makes it easier to test. Suppose I want to test the Get of PersonController. I used xUnit and Moq. To set this up
public class PersonServiceTester
{
private readonly Mock<IPersonService> _mockPersonService;
private readonly ILogger<PersonController> _testLogger;
private readonly PersonController _personController;
public PersonServiceTester()
{
_mockPersonService = new Mock<IPersonService>();
ILoggerFactory factory = new Microsoft.Extensions.Logging.Abstractions.NullLoggerFactory();
_testLogger = factory.CreateLogger<PersonController>();
_personController = new PersonController(_testLogger, _mockPersonService.Object);
}
...
}
Here _testLogger doesn’t do much because I choose not to test the ILogger functionality.
Here is my Fact in PersonServiceTest
[Fact]
public void PersonControllerTest()
{
List<Person> persons = new List<Person>();
persons.Add(new Person
{
Id = 1,
Name = "Herminia Mayert",
Address = "3 Rafaela Villages",
City = "3 Rafaela Villages",
Country = "Ghana"
});
_mockPersonService.Setup(s=>s.GetPersons()).Returns(persons);
IEnumerable actualPersons = _personController.Get();
Assert.Same(persons, actualPersons);
}
In this way, using the interface IPersonService makes it helpful to test the PersonController.
We see that the use of interfaces is a help to decouple classes.






