Moving from Django Apps to Services
-
Upload
craig-kerstiens -
Category
Documents
-
view
968 -
download
2
description
Transcript of Moving from Django Apps to Services
Django Apps to Services
Craig Kerstiens@craigkerstiens work at @heroku
Project A collection of configuration and apps for a particular Website.
(per Django Project)
Django
ProjectApp
A collection of configuration and apps for a particular Website.
A web application that does something. I.e. Weblog, Poll, Ticket system
(per Django Project)
Django
Table
Table
Table
Table
Table
Table
Table
Table Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
Table
App
Monolithic Monster
Monolithic Monster$ ls mysitemodels.pyviews.pyforms.pyadmin.py
Monolithic Monster$ ls mysitemodels.pyviews.pyforms.pyadmin.py
$ wc -l models.py 2557 models.py$ wc -l view.spy 14241 views.py
Monolithic Monster$ ls mysitemodels.pyviews.pyforms.pyadmin.py
$ wc -l models.py 2557 models.py$ wc -l view.spy 14241 views.py
http://www.slideshare.net/jacobian/the-best-and-worst-of-django
Within Django
Project
Within Django
Project
App
App
App
Within Django
Project
App
App
App
Tickets
FAQ
FaqCreator
Within Django
Project
App
App
App
Tickets
FAQ
FaqCreator
Within Djangocd myproject && find .manage.pyrequirements.txtsettings.pyurls.py./faq/admin.py./faq/forms.py./faq/models.py./faq/urls.py./faq/views.py./faqcreator/admin.py./faqcreator/forms.py./faqcreator/models.py./faqcreator/urls.py./faqcreator/views.py./tickets/admin.py./tickets/forms.py./tickets/models.py./tickets/urls.py./tickets/views.py
Reusability
cd myproject && find .Reusability
cd myproject && find .manage.pyrequirements.txtsettings.pyurls.py./faq/admin.py./faq/forms.py./faq/models.py./faq/urls.py./faq/views.py./faqcreator/admin.py./faqcreator/forms.py./faqcreator/models.py./faqcreator/urls.py./faqcreator/views.py./tickets/admin.py./tickets/forms.py./tickets/models.py./tickets/urls.py./tickets/views.py
Reusability
cd myproject && find .manage.pyrequirements.txtsettings.pyurls.py
cat myproject/requirements.txtfaq==0.1faqcreator==0.1 tickets==0.1
cd myproject && find .manage.pyrequirements.txtsettings.pyurls.py./faq/admin.py./faq/forms.py./faq/models.py./faq/urls.py./faq/views.py./faqcreator/admin.py./faqcreator/forms.py./faqcreator/models.py./faqcreator/urls.py./faqcreator/views.py./tickets/admin.py./tickets/forms.py./tickets/models.py./tickets/urls.py./tickets/views.py
-->
Reusability
REUSABILITY means faster features due to DRY (Don’t Repeat Yourself )
REUSABILITY means faster features due to DRY (Don’t Repeat Yourself )
REUSABILITY does not always mean SCALABILITY or MAINTAINABILITY
ProjectApp
A collection of configuration and apps for a particular Website.
A web application that does something. I.e. Weblog, Poll, Ticket system
(per Django Project)
Django
ProjectApp
A collection of configuration and apps for a particular Website.
A web application that does something. I.e. Weblog, Poll, Ticket system
Django
Service Method of communication over the web.Web APIs allow combination of multiple services
Tech imitate TeamsProject
App
App
App
Tickets
FAQ
FaqCreator
Company
Support
Knowledge Base
Support
Knowledge Base
Teams
App
App
Support
Knowledge Base
Teams Grow
App
App
Support
Knowledge Base
Billing
Teams Grow
App
App
Support
Knowledge Base
Billing
Marketing
Teams Grow
App
App
Support
Knowledge Base
Billing
Marketing
Analytics
Mobile
API
Front End
Social
Teams Grow
App
App
App
Support
Knowledge Base
Billing
Apps GrowMarketing
Analytics
Mobile
API
Front End
Social3 Apps
App
App
App
Support
Knowledge Base
Billing
Apps GrowApp
App
App
Marketing
Analytics
Mobile
App
App
App
API
Front End
Social9 Apps
Support
Knowledge Base
Billing
Apps GrowMarketing
Analytics
Mobile
API
Front End
Social
9 Apps
1 Codebase
SOA To the Rescue
SOA To the Rescue
SOA To the Rescue
Photo by Paul Downey (psd on flickr)
Defined contract for communicatingService
Defined contract for communicatingService
$ curl -O $FAQ_API/create/ -X “question=my\ question source=123”
In Python
In Python
data = {‘question’: “my question”,‘source’: 123}requests.POST(os.environ[‘FAQ_API’] + ‘/create/’, data=data)
More than just API
www.google.commail.google.comcalendar.google.com
More than just API
www.google.commail.google.comcalendar.google.com..............google.com
More than just API
Types of Services
site
Types of Services
sitesubdomain
Types of Services
sitesubdomainAPI
Types of Services
sitesubdomainAPI - no auth required
Types of Services
sitesubdomainAPI - no auth required - API auth required
Types of Services
sitesubdomainAPI - no auth required - API auth required - user auth required
Types of Services
App
App
App
Support
Knowledge Base
Billing
Where to StartApp
App
App
Marketing
Analytics
Mobile
App
App
App
API
Front End
Social
App
App
Support
Knowledge Base
Billing
Where to StartAppMarketing
Analytics
Mobile App
API
Front End
Social
1 JavascriptMany pages
LA POSTA
Project
App
App
App
Tickets
FAQ
FaqCreator
Within Djangocd myproject && find .manage.pyrequirements.txtsettings.pyurls.py./faq/admin.py./faq/forms.py./faq/models.py./faq/urls.py./faq/views.py./faqcreator/admin.py./faqcreator/forms.py./faqcreator/models.py./faqcreator/urls.py./faqcreator/views.py./tickets/admin.py./tickets/forms.py./tickets/models.py./tickets/urls.py./tickets/views.py
Apps Depend on Other Apps
Apps Depend on Other Appsfaq creator
Apps Depend on Other Appsfaq creator
faq app
Apps Depend on Other Appsfaq creator
ticket
faq app
Apps Depend on Other Appsfaq creator
ticket
faq app
faq app
Apps Depend on Other Appsfaq creator
faq app version == 0.1
faq app version == 0.2ticket
Apps Depend on Other Appspip install faq==0.1pip install faq==0.2pip freezefaq==0.2
Apps Depend on Other Appsfaq creator
faq app version == 0.1
faq app version == 0.2ticket
Back to APIs
What’s a serviceProviderEndpoint
API_HOST= http://127.0.0.1
/v1/create/
What’s a serviceProviderEndpoint
API_HOST= http://127.0.0.1
/v1/create/
Contract {‘question’: ‘foo bar’,‘source’: 123}
App
AppModelsViewsURLs
Service
AppModelsViewsURLs
Provider Endpoint Contract
A SERVICE means REUSABILITY and enables SCALABILITY and MAINTAINABILITY
Where to start
Where to start1. If you’re monolithic, break up the apps first
Where to start1. If you’re monolithic, break up the apps first2. Port something that doesn’t require user
Where to start1. If you’re monolithic, break up the apps first2. Port something that doesn’t require user3. Port where you have many dependencies
Porting an Appmodels.py
class Faq(models.Model): title = models.CharField(max_length=100) source = models.IntegerField(blank=True, null=True) description = models.TextField() solution = models.TextField() def __unicode__(self): return self.title
Porting an App
faqcreator/views.py
from faq.models import faq
i = Faq(title=‘foo’, description=‘bar’, solution=’la posta’)i.save()
Turn it into an API
Turn it into an API1. pip install django-tastypie
Turn it into an API1. pip install django-tastypie2. add tastypie to INSTALLED_APPS
Turn it into an API1. pip install django-tastypie2. add tastypie to INSTALLED_APPS3. define your APIs
Create your APIfaq/api.py
from tastypie.resources import ModelResourcefrom faq.models import Faqfrom django.contrib.auth.models import Userfrom tastypie.authentication import BasicAuthentication, DjangoAuthorizationfrom tastypie.authorization import Authorization
class FaqResource(ModelResource): class Meta: queryset = Faq.objects.all() resource_name = 'faq' list_allowed_methods = ['get', 'post'] authentication = BasicAuthentication() authorization = Authorization()
def obj_create(self, bundle, request=None, **kwargs): return super(FaqResource, self).obj_create(bundle, request, user=request.user)
def apply_authorization_limits(self, request, object_list): return object_list.filter(user=request.user)
Create your APIapi.py
class FaqResource(ModelResource): class Meta: queryset = Faq.objects.all() resource_name = 'faq' list_allowed_methods = ['get', 'post'] authentication = BasicAuthentication() authorization = Authorization()
def obj_create(self, bundle, request=None, **kwargs): return super(FaqResource, self).obj_create( bundle, request, user=request.user)
def apply_authorization_limits(self, request, object_list): return object_list.filter(user=request.user)
Create your APIapi.py
class FaqResource(ModelResource): class Meta: queryset = Faq.objects.all() resource_name = 'faq' list_allowed_methods = ['get', 'post'] authentication = BasicAuthentication() authorization = Authorization()
def obj_create(self, bundle, request=None, **kwargs): return super(FaqResource, self).obj_create( bundle, request, user=request.user)
def apply_authorization_limits(self, request, object_list): return object_list.filter(user=request.user)
Create your APIapi.py
class FaqResource(ModelResource): class Meta: queryset = Faq.objects.all() resource_name = 'faq' list_allowed_methods = ['get', 'post'] authentication = BasicAuthentication() authorization = Authorization()
def obj_create(self, bundle, request=None, **kwargs): return super(FaqResource, self).obj_create( bundle, request, user=request.user)
def apply_authorization_limits(self, request, object_list): return object_list.filter(user=request.user)
Create your APIapi.py
class FaqResource(ModelResource): class Meta: queryset = Faq.objects.all() resource_name = 'faq' list_allowed_methods = ['get', 'post'] authentication = BasicAuthentication() authorization = Authorization()
def obj_create(self, bundle, request=None, **kwargs): return super(FaqResource, self).obj_create( bundle, request, user=request.user)
def apply_authorization_limits(self, request, object_list): return object_list.filter(user=request.user)
Create your APIapi.py
class FaqResource(ModelResource): class Meta: queryset = Faq.objects.all() resource_name = 'faq' list_allowed_methods = ['get', 'post'] authentication = BasicAuthentication() authorization = Authorization()
def obj_create(self, bundle, request=None, **kwargs): return super(FaqResource, self).obj_create( bundle, request, user=request.user)
def apply_authorization_limits(self, request, object_list): return object_list.filter(user=request.user)
Create your APIapi.py
class FaqResource(ModelResource): class Meta: queryset = Faq.objects.all() resource_name = 'faq' list_allowed_methods = ['get', 'post'] authentication = BasicAuthentication() authorization = Authorization()
def obj_create(self, bundle, request=None, **kwargs): return super(FaqResource, self).obj_create( bundle, request, user=request.user)
def apply_authorization_limits(self, request, object_list): return object_list.filter(user=request.user)
Create your APIurls.py
from faq.api import FaqResourcefrom tastypie.api import Api
v1_api = Api(api_name='v1')v1_api.register(FaqResource())
urlpatterns = patterns('', url(r'^api/', include(v1_api.urls)),)
curl -H "Content-Type: application/json" --user admin:testing -X POST --data '{"title": "New FAQ", "source": "1", "description": "foo", "solution": "bar"}' http://127.0.0.1:8000/api/v1/faq/
Use your API
Use it in Python
Use it in Pythondata = { 'title': 'New FAQ', 'source': '1', 'description': 'foo', 'solution': 'bar'}
r = requests.post(os.environ[‘FAQ_API’] + "api/v1/faq/", data=data, auth=('admin', 'testing'))
Version Bump
Version Bumpdata = { 'title': 'New FAQ', 'source': '1', 'description': 'foo', 'solution': 'bar', 'related': ['123', '456']}
r = requests.post(os.environ[‘FAQ_API’] + "api/v2/faq/", data=data, auth=('admin', 'testing'))
Version Bump
data = { 'title': 'New FAQ', 'source': '1', 'description': 'foo', 'solution': 'bar', 'related': ['123', '456']}
r = requests.post(os.environ[‘FAQ_API’] + "api/v2/faq/", data=data, auth=('admin', 'testing'))
Version Bump
In our Applicationfaq_creator/views.py
ticket/views.py
r = requests.post(os.environ[‘FAQ_API’] + "api/v2/faq/", data=data, auth=('admin', 'testing'))
In our Application
r = requests.post(os.environ[‘FAQ_API’] + "api/v1/faq/", data=data, auth=('admin', 'testing'))
faq_creator/views.py
ticket/views.py
cd myproject && find .manage.pyrequirements.txtsettings.pyurls.py
cat myproject/requirements.txtfaq==0.1faqcreator==0.1 tickets==0.1
cd myproject && find .manage.pyrequirements.txtsettings.pyurls.py./faq/admin.py./faq/forms.py./faq/models.py./faq/urls.py./faq/views.py./faqcreator/admin.py./faqcreator/forms.py./faqcreator/models.py./faqcreator/urls.py./faqcreator/views.py./tickets/admin.py./tickets/forms.py./tickets/models.py./tickets/urls.py./tickets/views.py
-->
Reusability
cd myproject && find .
manage.pyrequirements.txtsettings.pyurls.py
cat myproject/requirements.txt
faq==0.1faqcreator==0.1 tickets==0.1
-->
Maintainability/Scalability/Agilityls projectsfaq-servicemyproject
cd faq-service && find .manage.pyrequirements.txtsettings.pyurls.py
cd myproject && find .manage.pyrequirements.txtsettings.pyurls.py
cat myproject/requirements.txtfaqcreator==0.1 tickets==0.1
Maintainability/Scalability/Agility
Maintainability/Scalability/AgilityFaq Project
- Its own Django project- 1 Django app- Own database- Own deployment
Maintainability/Scalability/AgilityFaq Project
- Its own Django project- 1 Django app- Own database- Own deployment
My Project- Contained Django Project- 2 Django apps- Own database- Own deployment
Take aways
Take aways1. Start non-critical (Cheat)2. Create services where theres app reuse3. Create services where theres pain4. Start small
Others
Others
1. Expect APIs to fail2. Flask is great for APIs3. Make it easy to setup/test
Fin.Resources
http://www.craigkerstiens.comhttp://bit.ly/djangoappstoservices/http://www.12factor.nethttp://github.com/craigkerstiens/