Social-media
Is a new version of code for my Social media app with Clean Architecture. I used most of Clean code tips with android, SOLID principles and design-patterns..
❤️
Clean Architecture
❤️
- I have written about how to architect android application using the Uncle Bob's clean architecture approach. and what's this architecture and why we should use an architecture here.
- And this an old project but i made it again with Uncle Bob's clean architecture approach Let's go and see what's the new here.
Clean Architecture maximizes the use of SOLID principles and we used all of them let's see
🏃
:
⭐
Single Responsibility
Each software component should have only one reason to change – one responsibility. So whatever class you have or whatever function you have these functions and classes should always only have one single responsibility and one reason to change.
for example getPosts function !
🤩
// I have one responsibility and it's just get important data from database like user data or posts
class RepositoryImp @Inject constructor(
private var database: Database
) : Repository {
// I have one responsibility and it's just get post from database and return list of posts
override suspend fun getPosts(): List<Post> = database.getAllPosts()
// I have one responsibility and it's just get current user from database and return it as User Object
override fun getUser(): User {
TODO("Not yet implemented")
}
}
⭐
Open-closed
- You should be able to extend the behavior of a component, without breaking its usage, or modifying its extensions.
- For example in this project we have a Database interface and DatabaseFromFirebase class which is extend or implement Database interface methods. Now if we want to modify something or add a function for example we can do in (Child)
👶 class. - Now you opened your Database interface to extention, anyone wants to add or modify something he will extend your Database interface and add what he want in his own child class
👶 (DatabaseFromFirebase class). and your class is closed to modification.
interface Database {
suspend fun setUserDataInfoOnDatabase(user: User)
suspend fun getCurrentUserData(id: String): User
suspend fun getAllUsers(): List<User>
suspend fun getAllPosts(): List<Post>
}
// this is your own implementation of the Database interface do whatever you want here
class DatabaseFromFirebase @Inject constructor(
private var databaseRef: DatabaseReference,
) : Database {
//extra functions ...
override suspend fun getAllPosts(): List<Post> {
return databaseRef.child(Constants.POSTS).get().await()
.children.map {
it.getValue(Post::class.java)!!
}
}
override suspend fun setUserDataInfoOnDatabase(user: User) {
databaseRef.child("users").child(user.id).setValue(user).await()
}
override suspend fun getCurrentUserData(id: String): User {
TODO("Not yet implemented")
}
override suspend fun getAllUsers(): List<User> {
TODO("Not yet implemented")
}
}
⭐
Liskov Substitution
If you have a class of one type, and any subclasses of that class, you should be able to represent the base class usage with the subclass, without breaking the app.
- We do that here when we inject RepositoryImp() from RepositoryImp (Child)
👶 Type whith provideMainRepository function which is return type of it Repository(Parent)👨 .
@Singleton
@Provides
fun provideMainRepository(
database: Database
): Repository = RepositoryImp(database) //function return type is Repository 'Parent' and this able to return RepositoryImp instead
- Now the parent class(Repository)
👨 is replaceable by their subclasses (RepositoryImp)👶 👶 and that without altering the behavior so that again
⭐
Interface Segregation
- It’s better to have many smaller interfaces than a large one, to prevent the class from implementing the methods that it actually doesn’t need.
- Don't force him
💪 to implement it😂 - Note we can do that by making a default body for this method in
Kotlin
like this:
interface RepositoryAuth {
fun notImportantForAll(){}
}
⭐
Dependency Inversion
Components should depend on abstractions rather than concrete implementations. Also higher level modules shouldn’t depend on lower level modules.
- See This example :
/* As a abstractions, if want to get the database from the Mars, I don't care just implement this interface and do what you want in your own class and suit concretion (firebase, api,etc..) */ interface Database { suspend fun setUserDataInfoOnDatabase(user: User) suspend fun getCurrentUserData(id: String): User suspend fun getAllUsers(): List<User> suspend fun getAllPosts(): List<Post> } // this is your own implementation of the Database interface do whatever you want here class DatabaseFromFirebase @Inject constructor( private var databaseRef: DatabaseReference, ) : Database { override suspend fun getAllPosts(): List<Post> { return databaseRef.child(Constants.POSTS).get().await() .children.map { it.getValue(Post::class.java)!! } } override suspend fun setUserDataInfoOnDatabase(user: User) { databaseRef.child("users").child(user.id).setValue(user).await() } override suspend fun getCurrentUserData(id: String): User { TODO("Not yet implemented") } override suspend fun getAllUsers(): List<User> { TODO("Not yet implemented") } } class DatabaseFromCustomApi : Database { override suspend fun setUserDataInfoOnDatabase(user: User) { TODO("Not yet implemented") } override suspend fun getCurrentUserData(id: String): User { TODO("Not yet implemented") } override suspend fun getAllUsers(): List<User> { TODO("Not yet implemented") } override suspend fun getAllPosts(): List<Post> { TODO("Not yet implemented") } }
- Now We can depended on abstractions (Database interface) and not on concretions (like Firebase or Custom Api) by this way. The Datebase interface now is a replaceable with its childern classes and we will make our reposiory class take Database interface as argument like this:
❤️
class RepositoryImp @Inject constructor(
private var database: Database //abstractions (firebase or your custom api)
// private var refDatabase: DatabaseReference // concretions (just for firebase)
) : Repository {
override fun getUser(): User {
TODO("Not yet implemented")
}
override suspend fun getPosts(): List<Post> = database.getAllPosts()
}
-
❤️ Now it's to easy if we want to convert from firebase to custom api and vice versa because our repository don't now what's firebase or what's api, Because it's take atractions not concretions .
Note
- And i achieve every principle and so imprtant design patterns too in this open source project, Don't forget check the it
❤️ - Don't forget support me by a star
⭐ for encourge me to write more articales ..