Spring Boot Feature Toggles Sinhala: Togglz භාවිතයෙන් Features Enable/Disable කරමු

Spring Boot Feature Toggles Sinhala: Togglz භාවිතයෙන් Features Enable/Disable කරමු

ආයුබෝවන් යාළුවනේ! කොහොමද ඉතින් ඔයාලට? අද අපි කතා කරන්න යන්නේ software development වල හරිම වැදගත්, ඒ වගේම හරිම ප්‍රයෝජනවත් සංකල්පයක් ගැන – ඒ තමයි Feature Toggles. විශේෂයෙන්ම, අපි මේක Spring Boot project එකකදී Togglz කියන library එක පාවිච්චි කරලා implement කරන්නේ කොහොමද කියලා බලමු.

අපි අලුත් feature එකක් හදලා production එකට deploy කරනකොට සමහර වෙලාවට පොඩි බයක් එනවා නේද? අලුත් feature එක නිසා දැනට තියෙන stable system එකට මොනවා හරි වෙයිද කියලා. ඒ වගේම, සමහර වෙලාවට අපි කැමතියි අලුත් feature එකක් userලා ටික දෙනෙක්ට විතරක් release කරලා බලන්න, එහෙමත් නැත්නම් A/B testing කරන්න. මේ හැම ප්‍රශ්නයකටම කදිම විසඳුමක් තමයි Feature Toggles කියන්නේ.

මේ tutorial එක අවසානෙදි, ඔයාලට පුළුවන් වෙයි:

  • Feature Toggles කියන්නේ මොනවාද සහ ඒවා වැදගත් ඇයි කියලා තේරුම් ගන්න.
  • Spring Boot project එකකට Togglz library එක integrate කරගන්න.
  • Togglz භාවිතයෙන් features define කරලා, runtime එකේදී ඒවා enable/disable කරගන්න.
  • ඔයාලගේ code එකේ feature Toggles implement කරන්නේ කොහොමද කියලා ප්‍රායෝගිකව ඉගෙන ගන්න.

හරි, එහෙනම් වැඩේට බහිමු!

Feature Toggles කියන්නේ මොනවාද? (What are Feature Toggles?)

සරලව කිව්වොත්, Feature Toggle එකක් කියන්නේ අපිට software එකක යම්කිසි feature එකක් runtime එකේදී enable කරන්න (ක්‍රියාත්මක කරන්න) හෝ disable කරන්න (අක්‍රිය කරන්න) පුළුවන් mechanism එකක්. මේක අපේ code එකේ 'if-else' condition එකක් වගේ ක්‍රියා කරනවා. ඒ කියන්නේ, feature එක active නම් එක්තරා code path එකක් run වෙනවා, නැත්නම් තව code path එකක් run වෙනවා.

ඇයි අපි Feature Toggles පාවිච්චි කරන්නේ? (Why use them?)

Feature Toggles නිසා ලැබෙන වාසි ගොඩයි. Developer කෙනෙක් විදියට ඔයාට මේවා ගොඩක් වැදගත් වෙයි:

  • A/B Testing: එකම feature එකේ version දෙකක් user group දෙකකට පෙන්නලා, මොන version එකද හොඳට වැඩ කරන්නේ කියලා බලන්න පුළුවන්. උදාහරණයක් විදියට, අලුත් checkout flow එකක් userලාගෙන් 10% කට විතරක් පෙන්නලා, ඒක කොච්චර සාර්ථකද කියලා බලන්න පුළුවන්.
  • Dark Launching: අලුත් feature එකක් develop කරලා production එකට deploy කරන්න පුළුවන්, හැබැයි මුලින්ම ඒක userලාට පෙන්නේ නැතුව තියාගන්න පුළුවන්. එතකොට අපිට පුළුවන් feature එක production environment එකේදී test කරලා, සම්පූර්ණයෙන්ම සූදානම් වුණාම enable කරන්න.
  • Gradual Rollouts: අලුත් feature එකක් එකපාරට හැමෝටම release කරනවට වඩා, මුලින්ම userලා ටික දෙනෙක්ට, පස්සේ තව ටික දෙනෙක්ට කියලා gradual විදියට release කරන්න පුළුවන්. මේකෙන් පුළුවන් අලුත් feature එකක් නිසා එන problems minimize කරගන්න.
  • Kill Switch: අලුත් feature එකක් release කලාට පස්සේ, ඒකෙ මොකක් හරි බරපතල bug එකක් හරි performance issue එකක් හරි ආවොත්, අපිට පුළුවන් ඒ feature එක වහාම disable කරන්න. මේකෙන් system එකේ downtime එක අඩු කරගන්න පුළුවන්.
  • Independent Deployments: අපිට පුළුවන් අලුත් feature එකක් develop කරලා deploy කරන්න, ඒක release කරන්නේ නැතුව. එතකොට developerලාට පුළුවන් තමන්ගේ code එක frequent විදියට main branch එකට merge කරන්න, merge conflicts අඩු කරගන්න පුළුවන්. Release date එක එනකම් feature එක disable කරලා තියන්න පුළුවන්.

දැන් ඔයාලට Feature Toggles වල වැදගත්කම තේරෙනවා ඇති. ඊළඟට අපි බලමු මේක Spring Boot project එකක Togglz භාවිතයෙන් implement කරන්නේ කොහොමද කියලා.

Togglz සමඟ Spring Boot වල Feature Toggles Implement කරමු (Let's Implement Feature Toggles in Spring Boot with Togglz)

Togglz කියන්නේ Java වලට තියෙන open source Feature Toggle library එකක්. Spring Boot එක්ක මේක integration කරගන්න එක හරිම ලේසියි.

1. Dependencies එකතු කිරීම (Adding Dependencies)

මුලින්ම ඔයාලගේ Spring Boot project එකේ pom.xml (Maven) හෝ build.gradle (Gradle) file එකට Togglz dependencies ටික එකතු කරගන්න ඕනේ.

Maven (pom.xml):

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Togglz Spring Boot Starter -->
    <dependency>
        <groupId>org.togglz</groupId>
        <artifactId>togglz-spring-boot-starter</artifactId>
        <version>3.2.0</version> <!-- නවතම version එක බලලා දාගන්න -->
    </dependency>

    <!-- Togglz Admin Console (optional, but highly recommended) -->
    <dependency>
        <groupId>org.togglz</groupId>
        <artifactId>togglz-console</artifactId>
        <version>3.2.0</version> <!-- නවතම version එක බලලා දාගන්න -->
    </dependency>
</dependencies>

Gradle (build.gradle):

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.togglz:togglz-spring-boot-starter:3.2.0' // නවතම version එක බලලා දාගන්න
    implementation 'org.togglz:togglz-console:3.2.0' // Optional, but recommended
}

togglz-console dependency එකෙන් අපිට Togglz Admin Console එකක් ලැබෙනවා. මේකෙන් අපිට පුළුවන් web interface එකක් හරහා features manage කරන්න. ඒක demo කරන්න වගේම real world usage වලටත් ගොඩක් පහසුයි.

2. Togglz Configuration

ඊළඟට අපි Togglz configure කරගන්න ඕනේ. මේකට ප්‍රධාන දේවල් තුනක් තියෙනවා:

  1. application.properties එකේ console path එක define කරන එක.
  2. අපේ features define කරන enum එකක් හදන එක.
  3. Togglz configuration class එකක් හදන එක.

2.1. Console Path එක Define කිරීම (Defining Console Path)

src/main/resources/application.properties file එකට පහත properties එකතු කරන්න:

togglz.console.enabled=true
togglz.console.path=/togglz

මේකෙන් කියන්නේ Togglz Admin Console එක /togglz කියන path එකෙන් access කරන්න පුළුවන් කියන එක. (උදා: http://localhost:8080/togglz)

2.2. Features Define කිරීම (Defining Features)

Togglz වල features define කරන්නේ enum එකක් විදියට. මේ enum එක org.togglz.core.Feature interface එක implement කරන්න ඕනේ.

අපි AppFeatures.java කියලා enum එකක් හදමු.

package com.example.togglzdemo;

import org.togglz.core.Feature;
import org.togglz.core.annotation.Label;

public enum AppFeatures implements Feature {

    @Label("නව වට්ටම් පද්ධතිය") // New Discount System
    NEW_DISCOUNT_SYSTEM,

    @Label("නව ගෙවීම් ක්‍රමය") // New Payment Method
    NEW_PAYMENT_METHOD,

    @Label("Product Search API V2") // Product Search API V2
    PRODUCT_SEARCH_V2;

    // පහසුව සඳහා isActive method එකක් මෙතනම දාගන්න පුළුවන්.
    // නමුත්, මෙය අනිවාර්ය නොවේ. FeatureManager හරහාද මෙය පරීක්ෂා කළ හැක.
    public boolean isActive() {
        return org.togglz.core.context.FeatureContext.getFeatureManager().isActive(this);
    }
}

මෙහිදී @Label annotation එකෙන් Admin Console එකේදී feature එකට display වෙන නම දෙන්න පුළුවන්. isActive() method එකෙන් අපිට පුළුවන් feature එක active ද කියලා පහසුවෙන් check කරන්න.

2.3. TogglzConfiguration Class එක (The TogglzConfiguration Class)

දැන් අපි Spring Boot Application එකට Togglz configure කරන්න TogglzConfig කියන Class එකක් හදමු.

package com.example.togglzdemo;

import org.springframework.context.annotation.Configuration;
import org.togglz.core.Feature;
import org.togglz.core.manager.TogglzFeatureManager; // Correct import for TogglzFeatureManager
import org.togglz.core.manager.EnumBasedFeatureProvider;
import org.togglz.core.manager.FeatureManager;
import org.togglz.core.manager.FeatureManagerBuilder;
import org.togglz.core.repository.StateRepository;
import org.togglz.core.repository.mem.InMemoryStateRepository;
import org.togglz.core.spi.FeatureProvider;
import org.togglz.core.user.UserProvider;
import org.togglz.servlet.util.ServletMetaDataUserProvider;

@Configuration
public class TogglzConfig implements org.togglz.core.manager.TogglzConfig { // Implement TogglzConfig interface

    @Override
    public Class getFeatureClass() {
        return AppFeatures.class; // අපි හදාගත්තු enum එක මෙතන දාන්න
    }

    @Override
    public StateRepository getStateRepository() {
        // Demo purposes සඳහා InMemoryStateRepository භාවිතා කරයි. Production සඳහා,
        // JDBCStateRepository, FileBasedStateRepository හෝ DynamoDBStateRepository වැනි
        // persistent repository එකක් භාවිතා කරන්න.
        return new InMemoryStateRepository();
    }

    @Override
    public UserProvider getUserProvider() {
        // Feature toggles user-specific කරන්න පුළුවන්. Simple demo එකකට ServletMetaDataUserProvider හොඳයි.
        // Production සඳහා, Spring Security වැනි user management system එකක් සමඟ integrate කරන්න පුළුවන්.
        return new ServletMetaDataUserProvider();
    }
}

මේ TogglzConfig class එකේ ප්‍රධාන methods තුනක් තියෙනවා:

  • getFeatureClass(): මේකෙන් කියන්නේ අපේ features අඩංගු enum එක මොකක්ද කියලා. අපේ එක AppFeatures.class.
  • getStateRepository(): මේක තමයි feature එකක state එක (enabled/disabled) save කරන්නේ කොහේද කියලා තීරණය කරන්නේ. Demo එකකට InMemoryStateRepository එක හොඳයි. හැබැයි production environment එකකදී server restart කරනකොට feature states නැතිවෙන්න පුළුවන්. ඒ නිසා production වලදී JDBCStateRepository (database එකක save කරන්න), FileBasedStateRepository (file එකක save කරන්න) හෝ cloud based repository (e.g., AWS DynamoDBStateRepository) එකක් වගේ persistent repository එකක් භාවිතා කරන්න.
  • getUserProvider(): සමහර වෙලාවට feature එකක් activate කරන්න ඕනේ specific user කෙනෙක්ට විතරක් වෙන්න පුළුවන්. මේ UserProvider එකෙන් පුළුවන් දැනට login වෙලා ඉන්න user කවුද කියලා Togglz ට කියන්න. Simple demo එකකට ServletMetaDataUserProvider එක හොඳයි. production වලදී Spring Security වගේ user management system එකක් එක්ක integrate කරලා custom UserProvider එකක් හදන්න වෙනවා.

3. Feature එකක් භාවිතා කරමු (Using a Feature)

දැන් අපි configure කරගත්තු features අපේ Spring Boot application එකේ code එකේදී භාවිතා කරන්නේ කොහොමද කියලා බලමු. මේකට ක්‍රම කිහිපයක් තියෙනවා:

3.1. FeatureManager Inject කිරීම (Injecting FeatureManager)

අපිට පුළුවන් FeatureManager bean එක අපේ Spring component වලට @Autowired කරන්න.

package com.example.togglzdemo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.togglz.core.manager.FeatureManager;

@Service
public class ProductService {

    @Autowired
    private FeatureManager featureManager;

    public String getProductDetails(String productId) {
        if (featureManager.isActive(AppFeatures.NEW_DISCOUNT_SYSTEM)) {
            // NEW_DISCOUNT_SYSTEM feature එක active නම්, අලුත් discount logic එක activate කරන්න.
            return "මෙය නව වට්ටම් පද්ධතියට අනුව ඇති product details: " + productId + ", මිල: " + calculateNewPrice(productId);
        } else {
            // නැත්නම්, පැරණි logic එක activate කරන්න.
            return "මෙය සාමාන්‍ය product details: " + productId + ", මිල: " + calculateOldPrice(productId);
        }
    }

    public String getPaymentOptions() {
        if (featureManager.isActive(AppFeatures.NEW_PAYMENT_METHOD)) {
            return "නව ගෙවීම් ක්‍රම (ක්‍රෙඩිට් කාඩ්, මොබයිල් පේමන්ට්, ආදිය)";
        } else {
            return "පැරණි ගෙවීම් ක්‍රම (කැශ් ඔන් ඩිලිවරි)";
        }
    }

    private double calculateNewPrice(String productId) {
        // අලුත් වට්ටම් logic එක මෙතන
        return 100.00;
    }

    private double calculateOldPrice(String productId) {
        // පැරණි මිල ගණනය කිරීමේ logic එක මෙතන
        return 120.00;
    }
}

මේ code එකෙන් පැහැදිලියි, featureManager.isActive(AppFeatures.NEW_DISCOUNT_SYSTEM) කියලා check කරලා, feature එක active නම් අලුත් logic එකත්, නැත්නම් පරණ logic එකත් run කරනවා.

3.2. @EnabledFeature සහ @DisabledFeature Annotations භාවිතා කිරීම (Using Annotations)

Spring components, විශේෂයෙන්ම Controllers හෝ Services වල methods specific features මත පදනම්ව enable/disable කරන්න අපිට පුළුවන් @EnabledFeature සහ @DisabledFeature annotations භාවිතා කරන්න.

package com.example.togglzdemo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.togglz.spring.web.annotation.EnabledFeature;
import org.togglz.spring.web.annotation.DisabledFeature;

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @Autowired
    private ProductService productService;

    @GetMapping("/{productId}")
    public String getProductInfo(@PathVariable String productId) {
        return productService.getProductDetails(productId);
    }

    // NEW_PAYMENT_METHOD active නම් පමණක් මේ endpoint එක වැඩ කරයි.
    @GetMapping("/checkout/new")
    @EnabledFeature(AppFeatures.NEW_PAYMENT_METHOD)
    public String newCheckoutProcess() {
        return "නව ගෙවීම් ක්‍රමයට සාදරයෙන් පිළිගනිමු! " + productService.getPaymentOptions();
    }

    // NEW_PAYMENT_METHOD active නැත්නම් පමණක් මේ endpoint එක වැඩ කරයි.
    @GetMapping("/checkout/old")
    @DisabledFeature(AppFeatures.NEW_PAYMENT_METHOD)
    public String oldCheckoutProcess() {
        return "පැරණි ගෙවීම් ක්‍රමය ඔස්සේ ඉදිරියට යමු! " + productService.getPaymentOptions();
    }

    // PRODUCT_SEARCH_V2 active නම් පමණක් මේ endpoint එක වැඩ කරයි.
    @GetMapping("/search/v2")
    @EnabledFeature(AppFeatures.PRODUCT_SEARCH_V2)
    public String searchProductsV2() {
        return "Product Search API V2 සක්‍රීයයි! වඩා හොඳ ප්‍රතිඵල.";
    }

    // PRODUCT_SEARCH_V2 active නැත්නම් පමණක් මේ endpoint එක වැඩ කරයි.
    @GetMapping("/search/v1")
    @DisabledFeature(AppFeatures.PRODUCT_SEARCH_V2)
    public String searchProductsV1() {
        return "Product Search API V1 සක්‍රීයයි! (පැරණි version).";
    }
}

මේ annotations මගින් අපිට පුළුවන් feature එකක් active ද නැද්ද කියන එක අනුව method එකක් expose කරන එක control කරන්න. ඒ කියන්නේ @EnabledFeature එකක් තියෙන method එකක්, අදාළ feature එක disable කරලා තියෙනවා නම්, Spring MVC මගින් map වෙන්නේ නැහැ. ඒ වෙනුවට HTTP 404 (Not Found) response එකක් එයි.

ප්‍රායෝගික උදාහරණයක් - E-commerce වට්ටම් Feature එකක් (A Practical Example - An E-commerce Discount Feature)

අපි හිතමු ඔයාලා E-commerce application එකක් හදනවා කියලා. ඒකෙදී අලුත් discount algorithm එකක් test කරන්න ඕනේ. මේකෙන් customerලාට වඩා හොඳ discounts ලැබෙනවා කියලා හිතමු, නමුත් production එකට දාන්න කලින් මේක පොඩි user group එකකට දීලා බලන්න ඕනේ. මේකට අපි Feature Toggle එකක් භාවිතා කරමු.

1. AppFeatures enum එකට අලුත් Feature එකක් එකතු කරන්න:

package com.example.togglzdemo;

import org.togglz.core.Feature;
import org.togglz.core.annotation.Label;

public enum AppFeatures implements Feature {

    @Label("නව වට්ටම් පද්ධතිය") // New Discount System
    NEW_DISCOUNT_SYSTEM,

    @Label("නව ගෙවීම් ක්‍රමය") // New Payment Method
    NEW_PAYMENT_METHOD,

    @Label("Product Search API V2") // Product Search API V2
    PRODUCT_SEARCH_V2,

    @Label("Advanced Discount Algorithm") // අලුත් feature එක
    ADVANCED_DISCOUNT_ALGORITHM;

    public boolean isActive() {
        return org.togglz.core.context.FeatureContext.getFeatureManager().isActive(this);
    }
}

2. DiscountService එකක් හදමු:

package com.example.togglzdemo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.togglz.core.manager.FeatureManager;

@Service
public class DiscountService {

    @Autowired
    private FeatureManager featureManager;

    public double calculateDiscount(String userId, double originalPrice) {
        if (featureManager.isActive(AppFeatures.ADVANCED_DISCOUNT_ALGORITHM)) {
            System.out.println("Advanced Discount Algorithm භාවිතා වේ.");
            // අලුත්, advanced discount logic එක
            return originalPrice * 0.20; // 20% discount
        } else {
            System.out.println("Standard Discount Algorithm භාවිතා වේ.");
            // පැරණි, standard discount logic එක
            return originalPrice * 0.10; // 10% discount
        }
    }
}

3. ProductController එකෙන් මේ DiscountService එක භාවිතා කරමු:

package com.example.togglzdemo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @Autowired
    private ProductService productService;

    @Autowired
    private DiscountService discountService;

    @GetMapping("/{productId}")
    public String getProductInfo(@PathVariable String productId) {
        return productService.getProductDetails(productId);
    }

    @GetMapping("/{productId}/discounted-price/{userId}")
    public String getDiscountedPrice(@PathVariable String productId, @PathVariable String userId) {
        double originalPrice = 200.00; // Product එකක මිලක්
        double discountAmount = discountService.calculateDiscount(userId, originalPrice);
        double finalPrice = originalPrice - discountAmount;

        String response = String.format("Product ID: %s, Original Price: %.2f, Discount Amount: %.2f, Final Price: %.2f",
                                        productId, originalPrice, discountAmount, finalPrice);
        return response;
    }
    
    // ... (අනෙකුත් endpoints මෙතනින් ඉවත් කර නැත, අවශ්‍ය නම් ඉහතින් ඇති product controller එකට මෙය එකතු කරන්න)
}

දැන් මේක Test කරමු! (Let's Test This Now!)

ඔයාලා Spring Boot Application එක run කරලා, http://localhost:8080/togglz කියන URL එකට යන්න. එතන ඔයාලට Togglz Admin Console එක පෙනෙයි. මේ console එකේදී ඔයාලට ADVANCED_DISCOUNT_ALGORITHM කියන feature එක enable හෝ disable කරන්න පුළුවන්.

Test Steps:

  1. Application එක Start කරන්න.
  2. Browser එකේ http://localhost:8080/togglz open කරන්න.
  3. ADVANCED_DISCOUNT_ALGORITHM feature එකේ Status එක DISABLED විදියට තියෙද්දී, මේ URL එකට යන්න: http://localhost:8080/api/products/product001/discounted-price/user123
    • Output එක: Product ID: product001, Original Price: 200.00, Discount Amount: 20.00, Final Price: 180.00 (10% discount)
  4. දැන් Togglz Admin Console එකට ගිහින් ADVANCED_DISCOUNT_ALGORITHM feature එක ENABLED කරන්න.
  5. නැවතත් http://localhost:8080/api/products/product001/discounted-price/user123 URL එකට යන්න.
    • Output එක: Product ID: product001, Original Price: 200.00, Discount Amount: 40.00, Final Price: 160.00 (20% discount)

දැක්කා නේද? අපිට code change නොකර, deploy නොකර, runtime එකේදී feature එකේ behaviour එක වෙනස් කරන්න පුළුවන් වුණා. මේක තමයි Feature Toggles වල බලය!

නිගමනය (Conclusion)

අද අපි Spring Boot project එකක Togglz භාවිතයෙන් Feature Toggles implement කරන්නේ කොහොමද කියලා විස්තරාත්මකව ඉගෙන ගත්තා. මේ සංකල්පය software development වලදී flexibility එක, ආරක්ෂාව සහ iteration speed එක වැඩි කරනවා.

Feature Toggles භාවිතයෙන් ඔයාලට පුළුවන් අවදානම අඩු කරලා, අලුත් features test කරන්න, gradual rollouts කරන්න, A/B testing කරන්න, ඒ වගේම ඕනෑම මොහොතක problematic feature එකක් disable කරන්න.

මේ tutorial එකෙන් ඔයාලට මේ ගැන හොඳ අවබෝධයක් ලැබෙන්න ඇති කියලා මම හිතනවා. දැන් ඔයාලට පුළුවන් මේ දැනුම ඔයාලගේම Spring Boot projects වලට integrate කරගන්න.

ඔබත් මේක ඔබේ project එකේ implement කරලා බලන්න! මොකද ඔයාලගේ අත්දැකීම්? අදහස් සහ ප්‍රශ්න comment section එකේ දාන්න. අපි ඊළඟ tutorial එකකින් නැවත හමුවෙමු! සුභ දවසක්!