Scalability Concepts A Friendly Guide for Software Developers (Estimated Read Time: 3 Minutes)

Scalability Basics:
Scalability refers to a system’s ability to handle an increasing workload by adding resources. It’s crucial for systems expected to grow.

Types of Scaling:

  1. Vertical Scaling (Scaling Up): Adding more power (CPU, RAM) to an existing machine. It’s simpler but has a limit.
  2. Horizontal Scaling (Scaling Out): Adding more machines to a system. It’s more complex but offers greater flexibility and capacity.

Key Principles:

  • Statelessness: Design components to be stateless to make scaling easier.
  • Load Balancing: Distribute traffic across multiple servers to ensure no single server is overwhelmed.
  • Caching: Store frequently accessed data in temporary storage to reduce load on primary resources.

Scalability in Cloud Platforms:
In cloud platforms, standardization and “economy of scale” principles drive hardware design. Simply adding resources to a single instance of a service may be limited. Therefore, adopting scale-out architectures and optimizing workloads to maximize resource utilization are crucial for achieving scalability. If you’re designing a new application or modifying an existing one before migrating, this can significantly reduce your cloud costs.

Challenges:

  • Data Consistency: Ensuring data remains consistent across multiple nodes.
  • Network Latency: More nodes can introduce latency due to increased communication.
  • Complexity: Managing a distributed system is inherently more complex.

Consistency in Databases:
Consistency ensures that data remains in a reliable state after interactions by applications and users. The underlying database engine governs how multiple concurrent activities from different users access data to maintain this consistency. Developers sometimes overlook consistency, assuming it’s optional. However, it’s essential because designing applications to handle concurrency anomalies in data is error-prone and time-consuming. Neglecting consistency can lead to performance issues, and developers end up writing code to address it. So, consistency should not be taken for granted.

Strategies:

  • Microservices: Break down applications into smaller, independent services.
  • Sharding: Split a database into smaller, more manageable pieces.
  • Replication: Duplicate data across multiple machines to improve read performance and reliability.

Research Insights:
Recent research from the University of Cambridge characterizes the importance of balancing scalability and consistency in modern systems. The study highlights that optimizing for one can often complicate the other, and achieving a balance requires careful design and trade-offs. Additionally, a survey published in the Journal of Internet Services and Applications emphasizes the challenges of achieving replica consistency in cloud environments. It categorizes methods into fixed, configurable, and monitoring approaches, discussing trade-offs between consistency and performance, and offering strategies to balance these aspects effectively.

Conclusion:
Scalability is essential for building robust, high-performance systems that can grow with your user base. By understanding and implementing effective scaling strategies, you can ensure your system remains responsive and reliable as demand increases. Achieving both scalability and consistency is crucial for robust systems. Scalability ensures efficient resource utilization, while consistency guarantees data accuracy and reliability.

For more in-depth insights, explore the System Design Primer on GitHub. For further research, check out the publication on replica consistency in cloud environments here.

References:

  1. System Design Primer on GitHub
  2. University of Cambridge Research on Scalability and Consistency
  3. Journal of Internet Services and Applications on Replica Consistency

Spring Boot – Code Layout & Structure

Spring Boot and it layout and code structure is something that is not predefined, it is up to the developer to follow the best practices to find the best practices available to them.

You may ask what are these best practices?

What typically occurs is that the project gets divided into layers, for example:

  • Web Layer
    • Controllers
    • Exception handlers
    • Filters
    • View templates
    • Requests and response objects
    • Etc
  • Service Layer
    • Takes data transfer objects (DTOs) as method parameters
      • And basic types
  • Repository Layer
    • Take entities as method parameters
      • And basic types

You can also divide a project into modules…

  • The first module is the data layer
  • The second module is the web layer
  • You can also divide the project into features!

Avoid default package

The default package does not include package declaration, it is not best practice to include a class in the default package.

Why you may ask?

It is because Spring boot scans the classes in the packages and sub-packages in annotations such as:

@SpringBootApplication
@ComponentScan
@EntityScan

And etc.

How about the Main Application Class, where does that go?

It is recommend to allocate the main application class in the root package, this usually has annotations like:

@SpringBootApplication
@ComponentScan
@EnableAutoConfiguration

It enables Spring to scan all classes in the root package and sub-packages. For example, if you are creating a JPA application, the MainApplication.java can be placed in the root package for example:

com.appsdeveloperblog.app.webservice.MainApplication.java

And all Web related classes in the sub-packages (ui) for example:

com.appsdeveloperblog.app.webservice.ui.controller
com.appsdeveloperblog.app.webservice.ui.filters

Service related sub-packages:

com.appsdeveloperblog.app.webservice.services

Repository layer related packages:

com.appsdeveloperblog.app.webservice.repositories

Popular layout structures

There are generally two approach developers gravitate towards on the structure they use for spring, this usually between:

  • Structure by Feature
  • Or.. structure by Layer

You may ask which one is best but this is preferential, below I will outline the benefits between each approach.

Structure by Feature

In this format all the classes are grouped into features, example:

com
 +- appsdeveloperblog
     +- app
         +- MainApplication.java
         |
         +- Student
         |   +- Student.java
         |   +- StudentController.java
         |   +- StudentService.java
         |   +- StudentRepository.java
         |
         +- order
             +- TimeTable.java
             +- TimeTableController.java
             +- TimeTableService.java
             +- TimeTableRepository.java

Benefits of structure by feature

  • ⭐️ Easy searching of classes by feature
  • ⭐️ Easy deletions of features
  • ⭐️ Testing and refactoring is simplified
  • ⭐️ Features can be shipped separately

Structure by Layer

In this approach, all the classes are separated as general layers, i.e. controllers, services, model, dtos, etc…

com
 +- appsdeveloperblog
     +- app
         +- MainApplication.java
         |
         +- domain
         |   +- Student.java
         |   +- TimeTable.java
         |
         +- controllers
         |     +- StudentController.java
         |     +- TimeTableController.java
         |
         +- services
         |    +- StudentService.java
         |    +- TimeTableService.java
         |
         +- repositories
              +- StudentRepository.java
              +- TimeTableRepository.java   

Although the structure by layer appears easy to locate classes, it does have disadvantages compared to structure by feature.👎

  • ❌ Features and modules cannot be shipped separately
  • ❌ Searching for classes that have a certain feature is harder
  • ❌ Refactoring on a certain feature is difficult, as the feature is located on every layer!
  • ❌ Can cause merge conflicts

Final note

To note, either approach can be feasible, you may find the structure by layer a good approach even with the highlighted down sides. For instance, if the spring boot application itself is dedicated to one specific feature, and you plan to separate these out into separate spring boot applications, this would negate the downsides, which in turn also has the added benefit of deploying individual micro-services per spring boot project.