DEV Community

Jose Aguilar
Jose Aguilar

Posted on

Separation of Concerns and Single Source of Truth — writing cleaner code.

In this post I am going to explain the principle of Separation of Concerns (SoC). Applying Separation of Concerns and Single Source of Truth to your applications makes your code easier to debug (there's no escaping it, sorry) and easier to maintain in the future. So let's dive right in...

What is Separation of Concerns?

Wikipedia defines Separation of Concerns as follows:

In computer science, separation of concerns is a design principle for separating a computer program into distinct sections. Each section addresses a separate concern, a set of information that affects the code of a computer program.

Separation of Concerns refers to abstracting away parts of a computer program into modules that deal with a single feature or behavior. In other words – each module should be responsible for one thing, and one thing only.

A classic example of Separation of Concerns is a web page, which is typically built using a combination of HTML, CSS, and JavaScript.

Consider the following example:

<html>
<head>
<style>
 body {background-color: black;}
 h1 {color: white;}
 p {color: blue;}
</style>
</head>
<body>

 <h1>Hello World!</h1>
 <p>Here is a paragraph</p>

</body>
<script>
console.log("Hello World!")
</script>
</html>
Enter fullscreen mode Exit fullscreen mode

This is perfectly working code that would give you a perfectly working page, but what if you need to add more code and your page grows in size? This is where things get messy, because our layers: page structure (HTML), styling (CSS) and behavior/logic (JavaScript) are all mixed in together in a single file. This makes scalability, maintainability, and debugging a lot messier down the line.

The right way to structure it would be something like this:

<!--index.html-->
<html>
 <head>
  <link rel="stylesheet" href="styles.css">
 </head>
 <body>

  <h1>Hello World!</h1>
  <p>Here is a paragraph</p>

 </body>
 <script src="logic.js"></script>
</html>
Enter fullscreen mode Exit fullscreen mode
/* styles.css */
body {background-color: black;}
h1   {color: white;}
p    {color: blue;}
Enter fullscreen mode Exit fullscreen mode
// logic.js
console.log("Hello World!")
Enter fullscreen mode Exit fullscreen mode

The three layers have been separated into their own files, making this code a lot cleaner. Each language is designed to handle a different concern of a website, with minimal overlap.

Image description

Source: https://www.srinadimpalli.com/2015/08/unobtrusive-javascript-layering-and-separation-of-concerns/

We see the same principle applied in React, where a UI is separated out into different components, each responsible for rendering a distinct part of an interface.

Image description

Source: https://www.techdiagonal.com/reactjs_courses/beginner/understanding-reactjs-components/

Each component is responsible for, or concerned with, only one thing.


Cohesion

Naturally, separation of concerns is a consequence of writing cohesive code, especially in Object-Oriented Programming.

For instance, suppose you have two separate functions like this:

function drawSquare() {
 //code to draw a square
}

function drawCircle() {
 //code to draw a circle
}
Enter fullscreen mode Exit fullscreen mode

These two functions are similar (or cohesive) enough that they can be grouped together under a single class, since they have a unifying purpose: to draw something.

class Draw {

  static drawSquare() {
    // code to draw a square
  }

  static drawCircle() {
    // code to draw a circle
  }
}

Draw.drawSquare() // draws a square
Draw.drawCircle() // draws a circle
Enter fullscreen mode Exit fullscreen mode

Separation of concerns allows data and methods related to a single purpose to take place within a class, where they are not only easy to keep track of but are also kept separate from other classes. If you need to change something, it's easier to keep track of what needs to change, since concerns are separated into cohesive classes, where data and logic resides in one place. In the programming world, this is also known as the Single-responsibility principle (SRP).


Single Source of Truth

Speaking of cohesiveness, there's another similar but equally important principle: Single Source of Truth (SSoT).

In the business world, the Single Source of Truth principle states that data within an organization should be aggregated into a central location, so that all users get their information from a single source, and that data should not be replicated anywhere else.

This applies to programming as well. Data that needs to be available to other parts of your application should come from a single source, and any changes to that data are reflected within that single source and nowhere else. This ensures that the data available to other components of your application is reliable and up to date.

If you've been coding in React, this should sound familiar. You've likely implemented state for a particular set of data (such as one fetched from an API) to be a single source of data for most, if not all of your application.

For example:

function App() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    const fetchUsers = async () => {
      const response = await fetch('https://jsonplaceholder.typicode.com/users'); //fetch data
      const userData = response.json();
      setPhotos(userData); // set state to fetched data
    }

    fetchUsers();
  })
}
Enter fullscreen mode Exit fullscreen mode

Here we fetched user data from a mock API, and set it to state so that it's the single source of user data for the rest of your React application.

So when your app renders additional components that need access to that data, you can pass that data from that single source as props to the rest of your components that need it.

function App() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    const fetchUsers = async () => {
      const response = await fetch('https://jsonplaceholder.typicode.com/users'); //fetch data
      const userData = response.json();
      setPhotos(userData); // set state to fetched data
    }

    fetchUsers();
  })

 return (
    <UsersContainer userData={users} />
  )
}
Enter fullscreen mode Exit fullscreen mode
// UsersContainer.js

function UsersContainer({ userData }) {
  return {userData}
}
Enter fullscreen mode Exit fullscreen mode

You get the idea.

This is also known as one way data binding. It means that there is only one place which represents state for this set of data, and your components listen to it. Any components that render data based on this state will only render different data if the data at the source is changed.

Another example is when you're dealing with forms. Suppose we want to build a form that gets data for its input fields from state, how would that be structured?

You could do this:

function Form() {
  // separate states for each input field
  const [name, setName] = useState("")
  const [address, setAddress] = useState("")
  const [phone, setPhone] = useState("")

  // set state for the name
  function handleName(event) {
    setName(event.target.value)
    };

  // set state for the address
  function handleAddress(event) {
    setAddress(event.target.value)
    };

  // set state for the phone number
  function handlePhone(event) {
    setPhone(event.target.value)
    };

   function handleSubmit(event) {
    //handle form submit logic here
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        name="name"
        onChange={handleName}
        value={address}
      />
      <input
        type="text"
        name="address"
        onChange={handleAddress}
        value={address}
      />
      <input
        type="text"
        name="phone"
        onChange={handlePhone}
        value={handlePhone}
      />
      <button type="submit">Submit</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

Notice how each input field has its own state, and the input fields within the form set state for their respective inputs. That would work, but it's pretty messy. Do all input fields need to have their own state? Or is there a way to refactor this so that not only does it make our code cohesive, but it makes our form look to a single source of truth for our data, as opposed to three separate sources.

Going back to the topic of cohesion, our name, address, and phone number states all serve the same purpose: to store data for one form. Should each piece of data have it's own state? Or can we have all of them together under one state?

We can re-factor as follows:

function Form() {
  // one state, a single source, for our form data.
    const [formData, setFormData] = useState({
      name: "",
      address: "",
      phone: "",
    })

    function handleChange(event) {
      const {name, value} = event.target

      setFormData({
        ...formData,
        [name]: value
      })
    }

   function handleSubmit(event) {
    //handle form submit logic here
  };

  return (
    // a single function for our onChange event, not 3 separate ones
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        name="name"
        onChange={handleChange}
        value={formData.name}
      />
      <input
        type="text"
        name="address"
        onChange={handleChange}
        value={formData.address}
      />
      <input
        type="text"
        name="phone"
        onChange={handleChange}
        value={formData.phone}
      />
      <button type="submit">Submit</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now, we've not only trimmed our code significantly. We've implemented a single source of truth for our form and made our logic more cohesive overall.


Conclusion

I hope you've gained a better understanding for the principles of Separation of Concerns and Single Source of Truth. Having organized, modularized code will always make your life easier down the road, as well as the lives of your peers :)

References:

*http://anh.cs.luc.edu/170/mynotes/cohesion.html
*https://en.wikipedia.org/wiki/Separation_of_concerns
*https://en.wikipedia.org/wiki/Cohesion_(computer_science)

Top comments (0)