Services
Do not get confused!
Services might be different than the very first thing that comes to mind. People might get confused about it. It is just a concept that is a little bit different in glim. It's not about web service building.
In glim framework, services can be used for seperating models from logic. Moreover, you can hold class wrappings in services such as mail sending, message queueing, etc. They can be defined as the reusable components that controllers can use. In a typical glim app, services reside in app/services.py
file.
Completely Optional
This layer is completely optional. You may want to move database logic through controllers or models. However, services are a good place to hold db logic and some wrappings of libraries.
The database logic can be seperated into number of functions in services. Consider an example of a user service. You can define a class namely "UserService" resides in app/services.py
;
# services.py
from glim_extensions.db import Orm
from app.models import User
class UserService:
@staticmethod
def auth(email, password):
user = Orm.query(User.id, User.email)
.filter_by(email=email, password=password)
return user
A Note on static functions
Since the services are mostly stateless. Therefore, it is clever to make the service functions as static. However, if you are hesitant about using static, you can use
classmethod
or just functions with no classes. The following example shows of a service just by defining functions;
# services.py
from glim_extensions.db import Orm
def auth_user(email, password):
user = Orm.query(User.id, User.email)
.filter_by(email=email, password=password)
return user
Where to use services and where not to ?
Since it's completely optional to make use of services, there are some guidelines about where to use services and where not to.
Where to use services ?
-
Reusable functions for controllers
Glim approach does not recommend reusability in the controller layer, meaning that if you
are calling reusable functions inside controllers, these functions should be on the services
layer. Glim recommends to move the logic to services. If you have different routes doing
different jobs, these controllers should be mapped into seperate functions.Consider a web project having secured and non-secured requests. Suppose that clients
can only see blog posts if they are authenticated. Moreover, they can only create blog
posts if they are authenticated. A bad example of implementing this would be the
following;
# routes.py
urls = {
'/login': 'AuthController.login',
'/post/create': 'PostController.create',
'/post/:<int:id>': 'PostController.get'
}
# controllers.py
from glim import Controller
from glim_extensions.db import Orm
class AuthController(Controller):
def login(self):
# perform login using Orm usage
def check_auth(self):
# perform auth checking using Orm usage
class PostController(Controller):
def create(self):
auth_controller = AuthController(self.request)
if auth_controller.check_auth():
# perform post creating
else:
# send unauthorized response
def get(self, id):
auth_controller = AuthController(self.request)
if auth_controller.check_auth():
# perform post getting
else:
# send unauthorized response
You might notice of a little bit dirty code here. It is, actually not clean. Instead of instantiating AuthController
, you can move this to the service layer;
# routes.py
urls = {
'/login': 'AuthController.login',
'/post/create': 'PostController.create',
'/post/<int:id>': 'PostController.get'
}
# services.py
from glim import Service
class AuthService(Service):
@staticmethod
def check(self):
# if user is auth, then return True
# else, return False
# controllers.py
from glim.component import Controller
from glim.db import Orm
from app.services import AuthService
class AuthController(Controller):
def login(self):
# perform login using Orm usage
class PostController(Controller):
def create(self):
if AuthService.check():
# perform post creating
else:
# send unauthorized response
def get(self, id):
if AuthService.check():
# perform post getting
else:
# send unauthorized response
Controllers should only dealing with requests and responses. It will be a little bit cleaner this way. This can be thought as angular.js's services.
The best way of auth checking would be in filters
The above example is also not a good practice. Use filters for auth checking instead!
-
Wrapping a class w/out any db operation
In glim, services are a good place to wrap classes and could be made static for easy to be
called from outside. If you consider an example of a mail sending logic, you would put this
logic inside servicesConsider an example of a system having approval mails after a successful registration.
This app would be the following;
# routes.py
urls = {
'/user/register': 'UserController.register',
'/user/approve': 'UserController.approve'
}
# services.py
from glim import Service
class MailService(Service):
@staticmethod
def send_approval_mail(from, to, body):
# perform mail sending
# return if mail is sent
# controllers.py
from glim import Controller
from app.services import MailService
class UserController(Controller):
def register(self):
# perform registering using services or just here
from = 'blah'
to = 'blah'
body = 'Welcome to glim my friend!'
result = MailService.send_approval_mail(from, to, body)
# return appropriate response
def approve(self):
# perform approval using services or just here
# return appropriate response
In this example, the mail service is used as a wrapper for a mail library. Therefore, as you might notice, services can be pretty useful when it comes to library wrapping.
Where not to use services ?
-
For simplicity, if you have not much reusable database logic.
Services can be pretty complex and useless when every single controller has its own
service. Therefore, you may want to move the database logic if it's not reusable in other
controllers. This can be done on small apps or even bigger apps. Services are for keeping
reusability in controller layer without creating functions inside controllers that are not
mapped to routes.However, if you need library mapping, services would still be the greatest place to hold that.
Again not a must, but recommended
These are just recommended practices glim framework is offering, you can make it different as much as you want.
Updated less than a minute ago