A deep dive into the critical aspects of becoming an effective technical leader and architect Introduction Venkat Subramaniam, a renowned software developer, consultant, and author, shares valuable insights into becoming an effective technical leader and architect in a recent The […]
Distributing SQL Databases Globally
I hope you enjoyed learning the article. Stay tuned! Subscribe to the new letter and The GeekNarrator youtube channel.
Cheers,
The GeekNarrator
Why is DynamoDB AWSome?
A cloud NoSQL database service that guarantees consistent performance at any scale.
Consistent Performance, Durability and High Availability are the key requirements for many mission critical use cases.
DynamoDB is also hassle-free because its fully managed and serverless.
Designing Instagram, Linkedin, Facebook like applications
Hey Everyone, Welcome to the first article of The GeekNarrator newsletter, I am excited to start this newsletter along with the release of my 35th episode. In this episode we have discussed on the most popular topic on earth “System […]
Test Driven Development with Frederik Banke
In this podcast we talked about TDD with an expert in the domain. Frederik has been in the industry for more than 15 years and he is an advocate of Clean Coding practices and teaches Software Engineers how to write […]
Distributed Stream Processing
In simple terms, when you are consuming events from a source, transforming them, doing some aggregation (probably maintaining some internal/external state) and then writing it to a sink. And more importantly all of this in realtime, at high scale.
Examples: Anomaly detection, analytics, alerting etc
Scaling Kafka Processing
🧐How do we scale Kafka processing ? Horizontally with number of consumers in a group. 🧐So can we have unlimited number of consumers? No, number_of_consumers <= number_of_partitions 🧐So can we have a very large number of partitions? ✌️How to calculate […]
What it takes to build a trading api like lemon.markets? An Insightful conversation with Max Linded (CEO, lemon.markets)
In this video I (Kaivalya Apte) talk to Max Linden, CEO and Co-Founder of Lemon.markets, where he shares his insights into entrepreneurship, product mindset, setting goals and making HUGE impact. – What is lemon.markets? – Why do we need a […]
Is Monolith Architecture Bad? Are Microservices Always Good? Conversation with Anant.
In this conversation we talk to Ananth about his experience with Microservices and Monlithic architectures. We also look at various examples of a good/bad monolith, good/bad micro services. We also talk about our personal experience with different architectural styles and […]
What is Decorator Pattern?
This pattern lets a programmer add more functionality to an object without impacting other objects of the class. So off course without inheritance
Why do you need it?
At times we need our objects to have more functionality based on different use cases. One way to achieve this is via Inheritance, but it is not always possible and it impacts all objects of the class, which might not be suitable.
Let’s build something in Scala to understand why we need it and how we can use Scala Traits to achieve it.
DB
trait represents a Key,Value Database with simple functions for fetching, updating and deleting data.
trait DB { def getData(k: String): String def putData(k: String, v: String): Unit def deleteData(k: String): Unit }
We have a SimpleDB
which implements this trait. It holds key -> value
pairs in memory using a simple mutable
map.
class SimpleDB extends DB { private val dbData = scala.collection.mutable .Map[String, String]() override def putData(key: String, value: String): Unit = { dbData.put(key, value) } override def deleteData(key: String): Unit = { dbData.remove(key) } override def getData(id: String): String = { dbData.get(id).getOrElse("no value found") } }
We would like to separate clients based on their access rights. Some clients may be read-only
access, some may also have write-access
and administrators can have delete-access
as well.
Let’s define some traits for different types of access.
Provides read access only
trait ReadOnly { val db: DB def getData(id: String): String = db.getData(id) }
Provides additional write access
trait Writer extends ReadOnly {
val db: DB
def deleteData(key: String) = db.deleteData(key)
def putData(key: String, value: String) = db.putData(key, value)
}
So now we have a simple database and some interfaces to access data based on access rights.
Let’s define simple clients to access our Database.
class DBClient(override val db: DB) extends ReadOnly
By default all clients will have a read-only
access.
Now how do we create clients with write access as well?
We have two options:
- Create a new client and extend
Writer
trait.- This is ok if you have only two such cases, but what if you have more flavors ? Will you create new clients for each one?
- On the same client extend
Writer
trait as well.- This means there won’t be any read-only client all will have write access as well.
- Also sometimes you won’t have control on changing the class definition, may be because its a third party library?
Third option goes something like this:
//Create a db val simpleDB = new SimpleDB //A read only client val readOnlyClient = new DBClient(simpleDB)
when we create a new client (read-only) using a with
keyword you can add additional feature of other Traits.
val writeableClient = new DBClient(simpleDB) with Writer
What this means is that only this object of DBClient
will have the write-access
without impacting any other object.
Just to have another example lets say to improve the performance of read calls we want to add caching functionality to our clients. How do we do that? Another trait and then mix it up when we need it.
Caching trait can add caching functionality to any client. mutable.Map
‘s getOrElseUpdate
takes a key and function to fetch the value if not already present in the cache. Here super.getData
will call ReadOnly clients getData
function
//Trait which adds a caching functionality trait Caching extends ReadOnly { //caches data val data = scala.collection.mutable.Map[String, String]() override def getData(key: String): String = { data.getOrElseUpdate(key, super.getData(key)) } }
Create a caching client as follows :
val cachingClient = new DBClient(simpleDB) with Caching
Test our clients:
readOnlyClient.getData("1") writeableClient.putData("3", "value of 3") readOnlyClient.getData("3") cachingClient.getData("3") writeableClient.deleteData("3") readOnlyClient.getData("3")
Note : This is just a simple implementation to demonstrate Scala mixin as Decorator pattern. The caching and database implementations are far from ideal and in real world we will have more sophisticated libraries to access database and implement caching. But it can still be taken as a general pattern to wrap underlying clients to add or hide functionality. Cheers, Kaivalya