Refactor a 6K Line of Code Domain Class

I have been working on a legacy project. It is a complex and big system. Throughout the codebase, there is a big domain object class.

How big is it, man?

Not much dude. It is just 6k (6.000) line of C# code.

One day, I found out a way to improve it. So the story begins.

Once Upon a Time

Imagine that class is School. School will have a collection of Students, Teachers, Labors, Rooms, Chairs, … The School class is a root domain. Therefore, they decided that all the methods must go through it.

There are typical patterns related to a collection: Add, Edit, Remove. Such as Add Student, Update Student, Remove Student, ..

It is a typical pattern we have seen many places. Except, that class became a ghost with 6.000 (6K) Line of Code. There are so many problems with such a codebase. But it is out of the scope of this post.

Pre-conditions

  1. Legacy code written with .NETt 4.0 (and still must be in 4.0).
  2. NHibernate as ORM framework.
  3. Look like DDD with CQRS (even none of them are actually true).
    public class School
    {
        private readonly IList<Student> _students;
        private readonly IList<Teacher> _teachers;
        private readonly IList<Room> _rooms;
        private readonly IList<Chair> _chairs;

        public virtual void AddStudent(Student student)
        {

        }

        public virtual Student FindStudent(Guid id)
        {
            return new Student();
        }

        public virtual void Update(Student student)
        {

        }

        public virtual void Remove(Student student)
        {

        }
    }

    public class Student
    {
        
    }

    public class Teacher
    {
        
    }

    public class Room
    {
        
    }

    public class Chair
    {
        
    }

There are a couple of things to remember first

  1. It is a legacy codebase. Which mean we did not design it in the first place. We took it over.
  2. Time is limited. Documentation is a luxury to have. In short, we cannot just rewrite or redesign it.
  3. It is painful.

How to refactor?

The goals should be:

  1. Reduce the number of line of code. Kill the ghost.
  2. Keep the visibility protection at the domain level. In DDD, the setters are protected or private.
  3. Improve responsibilities management.

Sometimes, Visual Studio has problems with opening the class 🙁 Poor it!

Superstar Needs Managers

The idea is quite simple. I got the inspiration from Superstar Metaphor. A superstar has many things: Properties, Cash, Connection, Style, Contracts, … Everything is attached to them. However, there is no way they can manage by themselves. They need managers. They need a manager for each aspect.

  • Property Manager: Manage properties
  • Financial Manager: Manage finance
  • Style Manager: Manage style
  • And so on.

Apply Composite Pattern

With that Superstar Metaphor, we can apply on School class. It is quite easy with some simple steps

Identify Areas

First, we have to identify areas that need outsource to managers. This step depends on your project context.

In our silly example, they are Student, Teacher, Room, and Chair.

Hide Managers

In Visual Studio (code) it is simply creating new classes 😛

We have StudentManager, TeacherManager, RoomManager, and ChairManager.

Delegate Responsibilities to Managers

Move the logic to proper managers and keep the old interface for School.

Show Me the Code

Yes, Sir!

    public class School
    {
        protected internal readonly IList<Student> _students = new List<Student>();
        private readonly IList<Teacher> _teachers = new List<Teacher>();
        private readonly IList<Room> _rooms = new List<Room>();
        private readonly IList<Chair> _chairs = new List<Chair>();

        private readonly StudentManager _studentManager;

        public School()
        {
            _studentManager = new StudentManager(this);
        }

        public virtual void AddStudent(Student student)
        {
            _studentManager.Add(student);
        }

        public virtual Student FindStudent(Guid id)
        {
            return new Student();
        }

        public virtual void Update(Student student)
        {

        }

        public virtual void Remove(Student student)
        {

        }
    }

    public class StudentManager
    {
        private readonly School _school;

        public StudentManager(School school)
        {
            _school = school;
        }

        public void Add(Student student)
        {
            _school._students.Add(student);
        }
    }

I applied the manager approach for Student area. The same goes for other areas: Teacher, Room, and Chair.

Protected Internal Modifier

Instead of using private or protected, we use protected internal modifier. With this modifier, we protect the domain from outside assembly. That is we just moved from class level to assembly level.

This modifier level allows managers to access Superstar’s properties.

Hey Cool Man!

Yeah. Even I said so to myself. Some of the immediate result I got after 2-3 hours

  1. Reduce more than 1K LoC in the Ghost class.
  2. Create managers to handle correct responsibilities.
  3. Feel a better control of the codebase
  4. Stop the trend of adding more code into the Ghost.

The best part is that, as a developer, I feel rocked 🙂 (yes, till now)__the feeling of getting a cool thing done.