
[ad_1]
Part 4 of 5

Now we are going to take a look at the data layer. For this it is important to remember the architecture design made by us on the second part of this series, You can read it here,
As you can see, we have three main modules: android
, iOS
And core
(Name shared
in Gradle).
xMain
our inside package shared
Modules for any platform you can think of, be it web, terminal, mac, etc.
But now let’s take a look at the most important of this fourth part:
commonMain/data
: Here is the data layer implementation for our project. Except for the platform implementation, all the code related to data access is being defined here: database engine, That is: repository interface implementation, local and remote data sources, platform data providers (these are expect classes which are being implemented in platform modules), etc.androidMain
: Contains code related to Android platform, it is inshared
because can be reusable for anyone android app module in the project. Here, we are going to define the implementation of expect classes IncommonMain
Modulus.iosMain
: stuffandroidMain
But for iOS.
important articles
androidMain
And iosMain
Modules inside shared modules do not implement the data access layer (repository or data source, as they must be reusable by another module), they implement the expected classes. For example:
- The way Android and iOS get current TIMESTAMP There’s a difference. We should create a requisition class that defines a contract to receive timestamps and each platform module implements it. The behavior is similar to that of an interface.
// inside the shared/commonMain module
expect class TimeProvider {
var timestamp: Long
}// implementation in shared/androidMain module
actual class TimeProvider {
actual var timestamp: Long = System.currentTimeMillis()
}// implementation in shared/iosMain module
actual class TimeProvider {
actual var timestamp = NSDate().timeIntervalSince1970.toLong()
}
The same applies for how each platform can create an instance of a database engine. We just define a “contract” to get the database driver:
expect class DatabaseDriverFactory {
fun createDriver(): SqlDriver
}
and each platform implements expected classFor example, Android has a . is required context
,
// implementation inside shared/androidMain module
actual class DatabaseDriverFactory(private val context: Context) {
actual fun createDriver(): SqlDriver {
return AndroidSqliteDriver(
schema = MoviesDb.Schema,
context = context,
name = "movies.db"
)
}
}
The data layer in our project is looking like this (as you can see, data
is in the package shared/commonMain
modulus):

we have domain package as the domain layer I explained in previous postAnd sqldelight
package out (right below the Kotlin package), and the data package where all the data layer is implemented.
Inside our data layer, we have:
local
package: local data access, here we can define a expected class For platform data access implementations (egSharedPreferences
in Android), or, as in our project, expected class To create a database driver (DatabaseDriverFactory.kt
,
MovieLocalDataSource
Responsible for CRUD operations related to local data sources (internal database, system preferences, etc.). It is being used in repository implementation (MovieRepositoryImpl
,remote
Package: As the name suggests, here we have all the code related to remote data access, ie: REST API, GraphQL, Firebase etc. We are using REST API only from MovieDB service. Also we can define responses and requests, these should be same for any platform, hence defined here.TimeProvider
AndPropertiesProvider
, expect classes which returns platform specific data,TimeProvider
Returns the current timestamp andPropertiesProvider
API KEY
To consume the REST API.MovieRepositoryImpl
: Implementation ofMovieRepository
Interface inside the domain layer. It is very easy to notice that it is shared by any platform: we request a list of movies, we ask inside our local data source if the data is out of date, if it is, we request from the REST API When we do this, we save the result to the local data source (that is, we update it) and return the updated data. it is being invoked byGetMoviesInteractor
inside our domain layer, andGetMoviesInteractor
must be injected into aViewModel
,Presenter
e.t.c.

Note: I recommend you to read SQDelight library documentation To understand how it works, it is easy to implement but there are some details worth knowing such as setting the same package structure to define the database scheme.
We have covered our data layer implementation in this Kotlin multiplatform project, the most important details are:
- We have implemented data layer in this
commonMain
modulus insideshared
module, as it is going to be reused by any platform. - To be more specific, classes in the data layer are injected into domain layer classes (such as inside use cases/interactors). However, on each platform is where we need to inject them.
- If our classes in the data layer require specific platform data, like
timestamp
value or api key, we will need to define expect classes and implement them in each platform module insideshared
Modulus. - Try to put all the data layer logic inside data package. How the repository works inside (such as what data sources it invokes or how it performs CRUD operations) may not work by platform.
[ad_2]
Source link
#Understanding #Layered #Architecture #KMM #Part #Data #Layer #Jose #Flavio #Quispe #Irazabal #August