İlkay İlknur

ASP.NET Core Uygulamalarında Azure App Configuration Kullanarak Feature Flags Implementasyonu

Temmuz 14, 2020

Bu yazıda konumuz ASP.NET Core uygulamalarında feature flag kullanımı. Feature flagler uygulamalarımız içerisinde istediğimiz featureları açıp kapamamızı sağlayan bir practice. Hem yeni geliştirilen featureların test edilmesi hem de development esnasında feature branchlerin uzun süre ana branch üzerinden uzak kalmasını engellemesiyle bizlere merge sorunları yaşamamak adına kolaylık sağlamakta. Bu yazımız daha çok uygulama kısmı ile ilgili olduğu için detaylı bilgi almak isteyenler için linki buraya bırakıyorum.

Azure App Configuration Servisi

Azure App Configuration servisi uygulamalarımız içerisindeki configleri merkezi bir yerden yönetmemizi sağlayan bir servis. Bu servisi kullanırken configleri merkezi bir yerden yönetmenin yanında tagleme, history tutma, karşılaştırma, Azure Key Vault entegrasyonu gibi özellikleriyle de işimizi oldukça kolaylaştırmakta. Bu servisin sağladığı özelliklerden biri de feature flag yönetimi. Şimdi gelin bu tarafa bir bakalım.

App Configuration servisini Azure portal üzerinde yarattıktan sonra soldaki menüden Feature Manager seçeneğine tıklayarak feature yönetimini yapabileceğimiz ekrana geçebiliriz.

Bu ekrana geçtikten sonra Add butonuna tıklayarak yeni featureları tanımlayabiliyoruz.

Yeni featureları ekledikten sonra listeleme ekranına döndüğümüzde şu şekilde bir listeyle karşılaşıyoruz. Bu liste üzerinden istediğimiz zaman ilgili featureları enable/disable edebiliyoruz.

Şimdi portali tarafını bırakıp kod tarafına geçelim ve portal üzerinde tanımladığımız featureları kod üzerinde nasıl kullanabiliyoruz ona bakalım.

Uygulamalarımız içerisinde hem App Configuration servisi hem de feature management altyapısıyla çalışabilmemiz için aşağıdaki nuget paketlerini projemize eklememiz gerekiyor.

  • Microsoft.Azure.AppConfiguration.AspNetCore
  • Microsoft.FeatureManagement.AspNetCore

İlk olarak Program.cs dosyasına gidip uygulamanın App Configurationı kullanması için gerekli olan tanımlamayı yapıyoruz.

public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
                {
                    var settings = config.Build();
                    config.AddAzureAppConfiguration(options =>
                    {
                        options.Connect(settings["ConnectionStrings:AppConfig"])
                            .UseFeatureFlags();
                    });
                })
                .UseStartup<Startup>());

Sonrasında uygulamamıza feature flags desteği ekliyoruz.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddFeatureManagement();
}

Son olarak ise feature flaglerin runtimeda otomatik olarak güncellenmesi için gerekli olan middleware'ı ekliyoruz ve konfigürasyon aşamasını bitiriyoruz.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseStaticFiles();
 
    app.UseRouting();
 
    app.UseAuthorization();
 
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
    app.UseAzureAppConfiguration();
}

Bu konfigürasyonla beraber artık asp.net core uygulamamıza hem config store hem de feature flag store olarak App Configuration servisini kullanacağımızı belirttik. Lokalde çalışırken configler için secret managerı kullanabilir, production ortamına geçerkende ConnectionStrings:AppConfig config değerine app configuration servisinin connection stringini koyarak kullanabiliriz.

Şimdi gelelim feature flag kullanım kısmına. En basit kullanımla uygulamalarımız içerisinde IFeatureManager'ı kullanarak feature'ın enabled olup olmadığı kontrol ederek gerekli aksiyonları alabiliyoruz.

public class HomeController : Controller
{
    private readonly IFeatureManager featureManager;
 
    public HomeController(IFeatureManager featureManager)
    {
        this.featureManager = featureManager;
    }
 
    public async Task<IActionResult> Index()
    {
        if (await featureManager.IsEnabledAsync("billingv3"))
        {
            return View(new HomeViewModel
            {
                Title = "You're using the new billing system!"
            });
        }
        else
        {
            return View(new HomeViewModel()
            {
                Title = "Welcome! Our new billing system coming soon!"
            });
        }
    }
}

Bu kullanımın yanında FeatureGate attribute'ünü kullanarak controller veya içerisindeki actionların belirli feature flaglerde çalışmasını sağlayabiliriz.

public enum FeatureFlags
{
    BillingV2,
    BillingV3
}
[FeatureGate(FeatureFlags.BillingV3)]
public class BillingController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

Yada sadece action bazlı olarak uygularsak.

public class BillingController : Controller
{
    [FeatureGate(FeatureFlags.BillingV3)]
 
    public IActionResult Index()
    {
        return View();
    }
}

View tarafına geçtiğimizde ise feature tagini kullanarak feature bazlı content gösterebiliyoruz. Bunun için ilk olarak _ViewImports.cshtml dosyasını açıp gerekli olan tag helperı eklememiz gerekiyor.

@addTagHelper *, Microsoft.FeatureManagement.AspNetCore

Artık viewlar içerisinde feature bazlı contenti aşağıdaki gibi gösterebiliriz.

<feature name="BillingV3" negate="true">
    <p>You're using the new billing system!</p>
</feature>

Feature taginde çoklu feature kontrolü yapmamız da mümkün.

<feature name="BillingV3,BillingV2" requirement="All">
    <p>You're using the new billing system!</p>
</feature>
<feature name="BillingV3,BillingV2" requirement="Any">
    <p>You're using the new billing system!</p>
</feature>

Eğer feature bazlı middleware eklemek istersek de şu şekilde bir kullanımda bulunabiliriz.

app.UseMiddlewareForFeature<VersionMiddleware>("billingv3");

Feature Flag Filters

Feature flagleri kullanırken built-in gelen filterları kullanabilir yada kendi filterlarımızı tanımlayabiliriz. Şu anda built-in olarak gelen iki filter var. Bunlar yüzde kaç oranında feature'ın enabled olacağını belirleyen Microsoft.Percentage diğeri de geçerlilik süresini belirten Microsoft.TimeWindow.

Bu yazıda ASP.NET Core uygulamalarında feature flag kullanımı ve Azure App Configuration servisiyle bu feature flaglerin yönetimini inceledik. Gördüğünüz gibi uygulamalarımıza feature flag yapısını entegre etmemiz oldukça kolay. Azure App Configuration servisini kullanmasanızda eklemiş olduğumuz nuget paketiyle config bazlı feature flag implementasyonu yapabilirsiniz.

Bir sonraki yazıda görüşmek üzere,