Bilgisayar Genel

Gerçek CRM Senaryolarıyla SOLID Prensipleri

Tarafından yazılmıştır Halil Durmuş

Kurumsal projelerde, özellikle CRM, ERP, mikroservis mimarileri ve entegrasyon katmanlarında ölçeklenebilir, sürdürülebilir ve test edilebilir kod yazmak kritik önem taşır. Ayrıca, SOLID prensipleri bunu sağlayan yazılım tasarımının temel taşlarını oluşturur. Bu nedenle, bu yazıda SOLID prensiplerini gerçek CRM ve mikroservis senaryoları üzerinden, hatalı kod → doğru kod örnekleri ile açıklıyorum.

Neden Kurumsal CRM Projelerinde SOLID Zorunludur?

Kurumsal CRM projelerinde kullanıcı sayısı, entegrasyon hacmi ve iş kuralları sürekli büyür. Bu nedenle SOLID prensipleri sadece “iyi uygulama” değildir; değişen iş kurallarına hızlı adapte olabilmek için zorunlu bir mimari disiplindir. Özellikle öğrenci, mezun, bursiyer ve okul gibi hiyerarşik yapılar SOLID uygulanmadığında hızla yönetilemez hâle gelir.

1) Single Responsibility Principle (SRP)

Bir sınıfın değişmesi için tek bir sebebi olmalı.

Gerçek Senaryo:

Dynamics 365’ten öğrenci bilgisi çekip CRM’e kaydediyoruz. Ama aynı sınıf içinde hem mapping yapılıyor hem de veri kaydediliyor hem de log atılıyor.

❌ Yanlış Tasarım

C#
public class StudentService
{
    public void SyncStudent(D365Student d365)
    {
        // Mapping
        var student = new Student
        {
            Name = d365.FullName,
            School = d365.SchoolName,
            ScholarshipType = d365.ScholarshipType
        };

        // Save
        Database.Save(student);

        // Log
        File.AppendAllText("log.txt", $"{student.Name} synced");
    }
}
C#

3 ayrı sorumluluk → değiştiğinde sınıf patlar.

✅ Doğru Tasarım (SRP uygulanmış)
C#
public class StudentMapper
{
    public Student Map(D365Student d365) =>
        new Student
        {
            Name = d365.FullName,
            School = d365.SchoolName,
            ScholarshipType = d365.ScholarshipType
        };
}

public class StudentRepository
{
    public void Save(Student student)
    {
        Database.Save(student);
    }
}

public class Logger
{
    public void Log(string message)
    {
        File.AppendAllText("log.txt", message);
    }
}

public class StudentSyncService
{
    private readonly StudentMapper _mapper;
    private readonly StudentRepository _repo;
    private readonly Logger _logger;

    public StudentSyncService(StudentMapper mapper, StudentRepository repo, Logger logger)
    {
        _mapper = mapper;
        _repo = repo;
        _logger = logger;
    }

    public void Sync(D365Student d365)
    {
        var student = _mapper.Map(d365);
        _repo.Save(student);
        _logger.Log($"{student.Name} synced");
    }
}
C#

2) Open/Closed Principle (OCP)

“Bir sınıf geliştirmeye açık, değişikliğe kapalı olmalı.”

Gerçek Senaryo:

Okul → fakülte → bölüm gibi hiyerarşik yapılarda farklı kaynaklardan veri geliyor.
Yeni kaynak eklenince servisi değiştirmemek istiyoruz.

❌ Yanlış Tasarım
C#
public class FacultyImporter
{
    public List<Faculty> Import(string sourceType)
    {
        if (sourceType == "D365")
        {
            // Dynamics 365 mapping
        }
        else if (sourceType == "SAP")
        {
            // SAP mapping
        }
        else if (sourceType == "CSV")
        {
            // CSV mapping
        }

        return new List<Faculty>();
    }
}
C#

Her yeni kaynakta else if ekleniyor → OCP ihlali.

✅ Doğru Tasarım: Strategy Pattern ile OCP
C#
public interface IFacultySource
{
    List<Faculty> Import();
}

public class D365FacultySource : IFacultySource
{
    public List<Faculty> Import()
    {
        // Mapping logic
        return new();
    }
}

public class CsvFacultySource : IFacultySource
{
    public List<Faculty> Import()
    {
        // CSV parsing logic
        return new();
    }
}

public class FacultyImporter
{
    private readonly IFacultySource _source;

    public FacultyImporter(IFacultySource source)
    {
        _source = source;
    }

    public List<Faculty> Import() => _source.Import();
}
C#

Yeni kaynak → Yeni sınıf ekleyip IFacultySource implement etmek yeterlidir.
Mevcut kod değişmez.

3) Liskov Substitution Principle (LSP)

“Türetilmiş sınıflar, temel sınıfın yerine kullanılabilmelidir.”

Gerçek Senaryo:

CRM’de Kullanıcı Türü hesaplayan bir servisimiz var:

  • Lise Öğrencisi
  • Üniversite Öğrencisi
  • Mezun

Yanlış kalıtım → alt sınıf davranışı bozuyor.

❌ Yanlış Tasarım
C#
public class UserTypeCalculator
{
    public virtual string CalculateType(User user) 
        => "GeneralUser";
}

public class GraduateUserTypeCalculator : UserTypeCalculator
{
    public override string CalculateType(User user)
    {
        throw new Exception("Graduate user cannot be processed here");
    }
}
C#

Ana sınıfın beklediği davranış alt sınıfta bozulmuş.

✅ Doğru Tasarım
C#
public interface IUserTypeCalculator
{
    string CalculateType(User user);
}

public class GraduateUserTypeCalculator : IUserTypeCalculator
{
    public string CalculateType(User user) 
        => "Graduate";
}

public class StudentUserTypeCalculator : IUserTypeCalculator
{
    public string CalculateType(User user) 
        => "Student";
}
C#

Artık tüm sınıflar aynı kontratı düzgün şekilde uyguluyor.

4) Interface Segregation Principle (ISP)

“Kullanmadığın metodu içeren interface’i implemente etme.”

Gerçek Senaryo:

CRM’de kullanıcıya SMS, mail veya push bildirim gönderen bir servis var. Ancak bazı entegre sistemler SMS desteklemiyor, bu yüzden tek bir arayüz tüm ihtiyaçları karşılamıyor.

❌ Yanlış Tasarım
C#
public interface INotificationService
{
    void SendEmail();
    void SendSms();
    void SendPush();
}

public class EmailService : INotificationService
{
    public void SendEmail() { }
    public void SendSms() => throw new NotImplementedException();
    public void SendPush() => throw new NotImplementedException();
}
C#

Kullanmadığımız metotları implement etmek zorunda kalıyoruz → ISP ihlali.

✅ Doğru Tasarım
C#
public interface IEmailNotification
{
    void SendEmail();
}

public interface ISmsNotification
{
    void SendSms();
}

public interface IPushNotification
{
    void SendPush();
}

public class EmailService : IEmailNotification
{
    public void SendEmail() { }
}

public class SmsService : ISmsNotification
{
    public void SendSms() { }
}
C#

Her servis sadece ihtiyacı olan interface’i implement eder.

5) Dependency Inversion Principle (DIP)

“Üst seviye sınıflar, alt seviye sınıflara değil soyutlamalara bağlı olmalı.”

Gerçek Senaryo:

CRM’e veri kaydeden bir servis var; ancak repository sınıfı doğrudan concrete bir sınıfa bağımlı olduğu için test yazılamıyor. Bu nedenle, yapı hem esnekliğini kaybediyor hem de bağımlılıklar artıyor.

Yanlış Tasarım (Test edilemez yapı)
C#
public class UserService
{
    private readonly UserRepository _repo = new UserRepository();

    public void Save(User user)
    {
        _repo.Save(user);
    }
}
C#

Unit test yazılamaz, çünkü yapı doğrudan gerçek database’e bağlıdır; bu yüzden test ortamında izole edilemez.

✅ Doğru Tasarım (Mocklanabilir ve test edilebilir)
C#
public interface IUserRepository
{
    void Save(User user);
}

public class UserRepository : IUserRepository
{
    public void Save(User user)
    {
        // DB logic
    }
}

public class UserService
{
    private readonly IUserRepository _repo;

    public UserService(IUserRepository repo)
    {
        _repo = repo;
    }

    public void Save(User user)
    {
        _repo.Save(user);
    }
}
C#

Artık test yazabiliriz:

C#
[Fact]
public void Save_Should_Call_Repository()
{
    var repo = new Mock<IUserRepository>();
    var service = new UserService(repo.Object);

    service.Save(new User());

    repo.Verify(x => x.Save(It.IsAny<User>()), Times.Once);
}
C#

SOLID Uygulamasının En Büyük Kazancı

SOLID uygulandığında kod tabanı öngörülebilir hale gelir. Böylece ekip değiştiğinde veya yeni bir modül eklendiğinde mevcut kodu kırma riski minimuma düşer. Entegrasyon projelerinde bu esneklik, değişiklik maliyetini ciddi şekilde azaltır.

Sonuç

SOLID prensipleri çoğu zaman teorik başlıklar gibi görünse de CRM yapıları, mikroservisler, entegrasyonlar ve kurumsal projelerde gerçek sorunları çözen pratik kurallardır:

  • SRP → Kod karmaşasını azaltır
  • OCP → Yeni özellik eklemeyi güvenli hâle getirir
  • LSP → Kalıtım kaynaklı hataları engeller
  • ISP → Sınıfları gereksiz bağımlılıklardan kurtarır
  • DIP → Test edilebilir, izole edilebilir bir yapı sağlar

Bu örneklerle proje kodlarınızı gözden geçirip daha sürdürülebilir bir mimari oluşturabilirsiniz.

Mini Check-List: Projen SOLID mi?

Bu örnekleri değerlendirirken kendi projenize şu soruları sorabilirsiniz: Bir sınıf tek bir işi mi yapıyor? Yeni bir kaynağı desteklemek için mevcut kodu değiştirmek zorunda kalıyor musunuz? Türeyen sınıflar beklenen davranışı bozuyor mu? Servisler gereksiz metodlara mı bağımlı? Birimi test etmek istediğinizde mock kullanabiliyor musunuz? Bu sorulara “hayır” cevabı verebildiğiniz her alan daha sürdürülebilir bir mimariye yaklaştığınız anlamına gelir.

Yazar hakkında

Halil Durmuş

1996 yılının Mart ayında Trabzon’da dünyaya geldim. Atatürk Üniversitesi, Bilgisayar Mühendisliği mezunuyum. Web sitemde ilgimi çeken konuları araştırarak yazılar paylaşıyorum.

Yorum Yap