惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
爱范儿
爱范儿
Attack and Defense Labs
Attack and Defense Labs
量子位
The GitHub Blog
The GitHub Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Scott Helme
Scott Helme
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 叶小钗
C
Cybersecurity and Infrastructure Security Agency CISA
S
Securelist
S
Schneier on Security
C
Cisco Blogs
B
Blog RSS Feed
Cisco Talos Blog
Cisco Talos Blog
Last Week in AI
Last Week in AI
WordPress大学
WordPress大学
腾讯CDC
酷 壳 – CoolShell
酷 壳 – CoolShell
罗磊的独立博客
Y
Y Combinator Blog
Latest news
Latest news
T
Tailwind CSS Blog
Jina AI
Jina AI
宝玉的分享
宝玉的分享
人人都是产品经理
人人都是产品经理
C
CERT Recently Published Vulnerability Notes
D
Darknet – Hacking Tools, Hacker News & Cyber Security
L
Lohrmann on Cybersecurity
The Cloudflare Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Microsoft Security Blog
Microsoft Security Blog
H
Help Net Security
P
Palo Alto Networks Blog
V
V2EX
博客园_首页
D
Docker
T
Threat Research - Cisco Blogs
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
Vulnerabilities – Threatpost
月光博客
月光博客
D
DataBreaches.Net
Stack Overflow Blog
Stack Overflow Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Know Your Adversary
Know Your Adversary
L
LangChain Blog
The Hacker News
The Hacker News
K
Kaspersky official blog
The Register - Security
The Register - Security
NISL@THU
NISL@THU

Learn Cloud Native

Agentgateway rate limiting for agents | Learn Cloud Native Local development with coding agents on Kubernetes using Signadot | Learn Cloud Native cuenv: one typed file for your whole project | Learn Cloud Native Preflight: AI Code Review Before You Push Anatomy of AI Agents Accessing Google Drive from Next.js Deploying to Fly.io using Dagger and Github Top Cloud-Native & Kubernetes Certifications [2026 Guide] Rapid microservices development with Signadot How to prepare for Istio certified associate exam (ICA) Global Rate Limiting in Istio with Envoy Rate Limit Service My Journey with Istio: From Incubation to Graduation Cilium Network Policy Tutorial: Secure Kubernetes Step by Step Kubernetes Networking: How kube-proxy and iptables Work Istio ServiceEntry: DNS vs. STATIC Resolution & Endpoints Explained Apply an Istio DestinationRule Globally (Mesh-Wide) Istio Rate Limiting: Configure a Local Rate Limiter in Envoy How to expose custom ports on Istio ingress gateway Portainer Tutorial: A Web UI for Kubernetes & Containers Traefik Proxy 2.x and TLS 101 Kubernetes CLI (kubectl) tips you didn't know about Setting up SSL certificates with Istio Gateway ArgoCD Best Practices You Should Know 在 OCI Ampere A1 计算实例上运行 AI Running AI On OCI Ampere A1 Instance How to Deploy Traefik Proxy Using Flux and GitOps Principles Firebase Emulators with Next.js: Local Setup Guide Running Hugo on free Ampere VM (Oracle Cloud Infrastructure) How to use kwatch to detect crashes in Kubernetes clusters Continuous profiling in Kubernetes using Pyroscope Monitoring containers with cAdvisor Creating a Kubernetes cluster in Google Cloud (LAB) Your first Kubernetes Pod and ReplicaSet (LABS) Container Lifecycle Hooks Maybe Convert Wasm Extension Config? GetIstio - CLI, training, and community Attach multiple VirtualServices to Istio Gateway Kubernetes Volumes Explained: Keep Data Beyond the Pod Send a Slack message when Docker images are updated Kubernetes Network Policy Ambassador Container Pattern Start Kubernetes Release Sidecar Container Pattern Kubernetes Init Containers Deploying multiple Istio Ingress Gateways The Strangler Pattern Kubernetes Development Environment with Skaffold Securing Kubernetes Ingress with Ambassador and Let's Encrypt All About the Ingress Resource How to quarantine Kubernetes pods? Getting started with Kubernetes Horizontal partitioning in MongoDB Docker image tagging scheme Six things to keep in mind when working with Dockerfiles Beginners guide to Docker Beginners guide to gateways and proxies Deploy and Operate Multiple Istio Meshes in one Kubernetes Cluster Managing service meshes with Meshery Circuit Breaking in Istio Explained Build and push your Docker images using Github Actions Kubernetes and Istio service mesh workshop materials Build Netlify-like deployment for React app using Kubernetes pods Six exciting enhancements in Istio 1.4.0 Fallacies of Distributed Systems CAP Theorem Explained Master the Kubernetes CLI (kubectl) - Cheatsheet Minikube Basics and How to Get Started with Kubernetes 5 Tips to Be More Productive with Kubernetes What are sticky sessions and how to configure them with Istio? Debugging Kubernetes applications using Istio Kubernetes Ingress and Istio Gateway Resource Zero Downtime Releases using Kubernetes and Istio Traffic Mirroring with Istio Service Mesh Expose a Kubernetes service on your own custom domain
Branch by Abstraction Pattern
Peter Jausovec · 2020-08-14 · via Learn Cloud Native

In the previous article, I wrote about the strangler pattern. The strangler pattern is useful for scenarios where you can intercept the calls at the edge of your monolithic application. In this article, I'll describe a pattern you can use when calls can't be intercepted at the edge.

Scenario

Let's consider a scenario where the functionality you're trying to extract is not called directly from the outside, rather it is being called from multiple other places inside the monolith.

branch by Abstraction Pattern
branch by Abstraction Pattern

In this case, you have to modify the existing monolith, assuming can do that and have access to the code. To minimize the disruptions to existing developers and make changes incrementally you can use the branch by abstraction pattern.

Note

You can get the sample code that goes with this article from Github

Steps

There are five steps to implementing the branch by abstraction pattern:

  1. Create the abstraction
  2. Use the abstraction
  3. Implement the new service
  4. Switch the implementation
  5. Clean up

Let's look at each step in more detail.

1. Create the abstraction

1. Create the abstraction
1. Create the abstraction

The first step is to create an abstraction for the functionality you are extracting. In some cases, you might already have an interface in front of the functionality, and that is enough. If you don't however, you will have to search the code base and find all places where the functionality you're trying to extract is being called from.

As an example, let's consider we are trying to extract the functionality that sends a notification, that looks like this:

function sendNotification(email: string): Promise<string> {
  // code to send the notification
}

As part of the first step, you will create an interface that describes the functionality, something like this:

export interface Notifications {
  sendNotification(email: string): Promise<string>;
}

2. Use the abstraction with the existing implementation

2. Use the abstraction with the existing implementation
2. Use the abstraction with the existing implementation

Once you put the abstraction in place you can refactor existing clients/callers to use the new abstraction point, instead of directly calling the implementation. The nice thing here is that all these changes can be done incrementally. At this point, you haven't made any functional changes per-se, you only re-routed the calls through the abstraction.

Practically speaking at this point you will have to create an implementation for the Notifications interface you created in the previous step. Here's an example of how you'd do that:

class NotificationSystem implements Notifications {
  sendNotification(email: string): Promise<string> {
    // Existing implementation
  }
}

In addition to implementing the interface, you should also consider creating a function that returns the Notifications interface with the desired implementation. At first, you will only have a single implementation, the NotificationSystem class above, but later you will use the same function to implement a feature flag that will allow you to switch between different implementations. For now, the newNotificationSystem would look like this:

export function newNotificationSystem(): Notifications {
  return new NotificationSystem();
}

With this in place, you can search the code base for any calls to sendNotification function and replace it with newNotificationSystem().sendNotification(...).

3. Implement the new service

3. Implement the new service
3. Implement the new service

The existing functionality is now being called behind the abstraction and you can independently work on implementing the new service that will have the same functionality. Similarly, as you would do it with the strangler pattern, you can deploy the new service to production, but don't release it yet (no traffic is being sent to it). This allows you to test the new service and ensure it works as expected.

You can also spend time in this step to create the new implementation of the Notifications abstraction, that will call this new service. For example, something like this:

class ServiceNotificationSystem implements Notifications {
  async sendNotification(email: string): Promise<string> {
    const serviceUrl = process.env.NOTIFICATION_SERVICE_URL;
    if (serviceUrl === undefined) {
      throw new Error(
        `NOTIFICATION_SERVICE_URL environment variable is not set`
      );
    }
    const response = await fetch(serviceUrl, {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ email }),
    });
    return response.json();
  }
}

Similarly, you can start updating the newNotificationSystem function and put the new implementation under a feature flag.

Note

Feature flags, also known as toggles or switches, allow you to switch certain functionality on or off through the use of configuration settings/environment variables, without redeploying the code.

Here's how the simple feature flag implementation might look like for our case:

export function newNotificationSystem(): Notifications {
  const useNotificationSystem = process.env.USE_NOTIFICATION_SYSTEM_SERVICE;

  // If variable is  set -> use the new implementation
  if (useNotificationSystem !== undefined) {
    console.log('Using service (new) implementation');
    return new ServiceNotificationSystem();
  }

  console.log('Using existing (old) implementation');
  return new NotificationSystem();
}

We are checking if the USE_NOTIFICATION_SYSTEM_SERVICE environment variable is set, and if it is we return the new implementation (ServiceNotificationSystem). If the environment variable is not set we return the existing (old) implementation (NotificationSystem).

4. Switch the implementation

4. Switch the implementation
4. Switch the implementation

You have deployed the new service and you have a feature flag in place that allows you to switch between implementation. You can now flip the switch (or set the environment variable in our case) to start using the new service implementation, instead of the old implementation.

You would continue to monitor the new service to make sure everything is working as expected. If you discover any issues you can revert to the previous implementation by simply removing that environment variable. The nice thing about this pattern is that you aren't locking yourself out of anything and at each step there's an 'escape hatch' you can take in case something goes wrong.

5. Clean up

5. Clean up
5. Clean up

The final step is to clean up. Since the old implementation is not being used anymore, you can completely remove it from the monolith. Don't forget to remove any feature flags as well. You could also remove the abstraction, but I think leaving it in place doesn't hurt anything.

Conclusion

The branch by abstraction pattern can be extremely valuable when you're trying to extract functionality that's deep inside the monolith and you can't intercept the calls to it at the edge of the monolith. The pattern allows for incremental changes that are reversible in case anything goes wrong.