Interface

Zapętlone projekty? – rozwiązaniem interfejs

Posted on

Mam projekt Persistence, który odpowiada za połączenie interfejsów repozytoriów z EntityFrameworkiem (Architektura heksagonalna). Dużą częścią tego projektu są SampleData. Są to dane, którymi seedujemy baze za starcie, aby można było wyklikać skomplikowane scenariusze na podczas Demo, oraz aby na nich można było odpalać UI testy w Selenium. (aplikacja pobiera dane z innych źródeł więc niemożliwe jest wypełnienie danych poprzez wyklikanie z przeglądarki).

Tak wyglądał kod, gdy wszystko było w jednym projekcie:

class MyContext : DbContext
{
    public DbSet<User> Users { get; set; }
}

class MigrationsConfiguration : DbMigrationsConfiguration<MyContext>
{
    protected override void Seed(MyContext context)
    {
        SeedConfiguration.Seed(context);
    }
}

class SeedConfiguration
{
    public static void Seed(MyContext context)
    {
        var user = new User
        {
            Id = 1,
            UserName = "John Galt"
        };

        context.Users.AddOrUpdate(x => x.Id, user);
    }
}

Ten SampleData był już osobnym katalogiem i zaczał z czasem bardzo rosnąć. Stało się jasne, że Persistence sam w sobie bardzo rzadko się zmienia (czasem jakieś migracje, nowe pola), czyli jest “stabilny”. Natomiast SampleData było żywe (nowe rzeczy, refactoringi itp).

Jest to więc znakomity kandydat do wydzielenia nowego projetku. I tutaj pojawił się problem. Persistence potrzebowało mieć referencję do SampleData, aby wywołać explicite metodę Seed(), a SampleData potrzebowało referencję do Persistence aby mieć DbContext na którym operuje.

Tego problemu nie zauważyłem od razu, dopiero na końcu wydzielania nowego projektu uderzyło mnie to. Cięzko było to obejść :/ Próbowałem coś w stylu ładowanie dynamiczne dll’ek z katalogu i skanowanie refleksja kto implementuję metodę którą chcemy wywołać – wszystko HACKy. Nie warto robić hacków więc zostawiłem temat. Po miesiącu rozwiązanie samo mnie uderzyło gdy wcale o nim nie myślałem 🙂 i był to nowy interfejs.

Kod gdy mamy dwa projekty i Persistence ma referencję do SampleData

// SampleData project
public interface ISampleDataContext
{
    DbSet<User> Users { get; set; }
}

class SeedConfiguration
{
    public static void Seed(ISampleDataContext context)
    {
        var user = new User
        {
            Id = 1,
            UserName = "John Galt"
        };

        context.Users.AddOrUpdate(x => x.Id, user);
    }
}

// Persistence project
class MyContext : DbContext, ISampleDataContext
{
    public DbSet<User> Users { get; set; }
}

class MigrationsConfiguration : DbMigrationsConfiguration<MyContext>
{
    protected override void Seed(MyContext context)
    {
        // works cause MyContext implements ISampleDataContext
        SeedConfiguration.Seed(context);
    }
}

Czyli eleganckie rozwiązanie zgodne z OOP.

Od siebie mogę dodać, że nawet jeśli wydzieleniu tego projetku było ważną rzecza (nie tylko ja widziałem tą potrzebę) to nie warto było robić z tego powodu dirty HACKA. Czasem lepiej poczekać, pogadać z różnymi ludżmi… Czasem ktoś nam poda dobre rozwiązanie a czasem ono samo do nas przyjdzie.

Advertisements