OpenSky is a digital agency that uses Symfony to power multiple e-commerce businesses. It has over 100 employees across several global offices. OpenSky migrated from Magento to Symfony2 in 2010 and has been an early adopter of new Symfony versions. Their coding workflow utilizes Git, GitHub Enterprise, PHP coding standards, and peer code reviews. They implement security using multiple firewalls and role voters. Doctrine is used for both MongoDB and MySQL, with references between the databases. Events and queues are used for asynchronous functionality.
3. Symfony @ OpenSky
What is OpenSky?
• Multiple businesses powered by Symfony:
1. opensky.com
2. dotandbo.com
3. 55mulberry.com
4. Symfony @ OpenSky
What is OpenSky?
• Multiple businesses powered by Symfony:
1. opensky.com
2. dotandbo.com
3. 55mulberry.com
• +100 employees
5. Symfony @ OpenSky
What is OpenSky?
• Multiple businesses powered by Symfony:
1. opensky.com
2. dotandbo.com
3. 55mulberry.com
• +100 employees
• Offices all around the world in New York,
Portland, Nashville, Miami, Chico,
Bucharest and Los Angeles
8. Symfony @ OpenSky
• Early adopter of Symfony2 in 2010 before 2.0
stable was released
History
9. Symfony @ OpenSky
• Early adopter of Symfony2 in 2010 before 2.0
stable was released
• Migrated from Magento -> symfony1 ->
Symfony2
History
10. Symfony @ OpenSky
• Early adopter of Symfony2 in 2010 before 2.0
stable was released
• Migrated from Magento -> symfony1 ->
Symfony2
• Aspects of Magento cart utilized throughout
migration process until completely phased
out with proprietary cart/checkout technology
History
28. Symfony @ OpenSky
• PHP7 Type-Hints when possible
• DocBlocks only when adds clarity
Coding workflow
29. Symfony @ OpenSky
• PHP7 Type-Hints when possible
• DocBlocks only when adds clarity
• ClassName::class
Coding workflow
30. Symfony @ OpenSky
• PHP7 Type-Hints when possible
• DocBlocks only when adds clarity
• ClassName::class
• ‘NamespaceClassName’
Coding workflow
31. Symfony @ OpenSky
• PHP7 Type-Hints when possible
• DocBlocks only when adds clarity
• ClassName::class
• ‘NamespaceClassName’
• In forms use class constant
Coding workflow
32. Symfony @ OpenSky
• PHP7 Type-Hints when possible
• DocBlocks only when adds clarity
• ClassName::class
• ‘NamespaceClassName’
• In forms use class constant
• $builder->add('name', TextType::class);
Coding workflow
33. Symfony @ OpenSky
• PHP7 Type-Hints when possible
• DocBlocks only when adds clarity
• ClassName::class
• ‘NamespaceClassName’
• In forms use class constant
• $builder->add('name', TextType::class);
• Always use ‘use’ statement
• $time = new DateTime()
Coding workflow
40. Symfony @ OpenSky
• Split things into broad bundles. Don’t over
bundle.
Symfony Bundles
41. Symfony @ OpenSky
• Split things into broad bundles. Don’t over
bundle.
• MainBundle
Symfony Bundles
42. Symfony @ OpenSky
• Split things into broad bundles. Don’t over
bundle.
• MainBundle
• AdminBundle (500+ routes, 300 extra
services in the container)
Symfony Bundles
43. Symfony @ OpenSky
• Split things into broad bundles. Don’t over
bundle.
• MainBundle
• AdminBundle (500+ routes, 300 extra
services in the container)
• APIBundle
Symfony Bundles
44. Symfony @ OpenSky
• Split things into broad bundles. Don’t over
bundle.
• MainBundle
• AdminBundle (500+ routes, 300 extra
services in the container)
• APIBundle
• 3rd Party Bundles
Symfony Bundles
45. Symfony @ OpenSky
• Split things into broad bundles. Don’t over
bundle.
• MainBundle
• AdminBundle (500+ routes, 300 extra
services in the container)
• APIBundle
• 3rd Party Bundles
• AvalancheImagineBundle
Symfony Bundles
46. Symfony @ OpenSky
• Split things into broad bundles. Don’t over
bundle.
• MainBundle
• AdminBundle (500+ routes, 300 extra
services in the container)
• APIBundle
• 3rd Party Bundles
• AvalancheImagineBundle
• FOSUserBundle
Symfony Bundles
47. Symfony @ OpenSky
• Split things into broad bundles. Don’t over
bundle.
• MainBundle
• AdminBundle (500+ routes, 300 extra
services in the container)
• APIBundle
• 3rd Party Bundles
• AvalancheImagineBundle
• FOSUserBundle
• Using 3rd party bundles may impact
maintainability in the long term
Symfony Bundles
50. Symfony @ OpenSky
• MongoDB and MySQL together
• References between MongoDB and MySQL
Doctrine
51. Symfony @ OpenSky
• MongoDB and MySQL together
• References between MongoDB and MySQL
• Using the Doctrine ORM & MongoDB ODM to
make the models for the two databases
transparent to the application.
Doctrine
52. Symfony @ OpenSky
• MongoDB and MySQL together
• References between MongoDB and MySQL
• Using the Doctrine ORM & MongoDB ODM to
make the models for the two databases
transparent to the application.
• Never delete data due to referential integrity
problems. Use soft delete (deletedAt)
Doctrine
53. Symfony @ OpenSky
• MongoDB and MySQL together
• References between MongoDB and MySQL
• Using the Doctrine ORM & MongoDB ODM to
make the models for the two databases
transparent to the application.
• Never delete data due to referential integrity
problems. Use soft delete (deletedAt)
• Traits for common functionality like createdAt/
updatedAt/deletedAt
Doctrine
56. Symfony @ OpenSky
• How long did it take to run?
• Is it backwards compatible?
Doctrine Migrations
57. Symfony @ OpenSky
• How long did it take to run?
• Is it backwards compatible?
• Up & Down
Doctrine Migrations
58. Symfony @ OpenSky
• How long did it take to run?
• Is it backwards compatible?
• Up & Down
• schema.sql / data.sql up-to-date
Doctrine Migrations
59. Symfony @ OpenSky
• How long did it take to run?
• Is it backwards compatible?
• Up & Down
• schema.sql / data.sql up-to-date
• Add indexes
Doctrine Migrations
60. Symfony @ OpenSky
• How long did it take to run?
• Is it backwards compatible?
• Up & Down
• schema.sql / data.sql up-to-date
• Add indexes
• Remove data later in separate
migration
Doctrine Migrations
64. Symfony @ OpenSky
• Lean
• No Business Logic
• Only Flow control
Controllers
65. Symfony @ OpenSky
• Lean
• No Business Logic
• Only Flow control
• Extend Custom BaseController
Controllers
66. Symfony @ OpenSky
• Two tiers of authentication implemented using role
voters.
Security
67. Symfony @ OpenSky
• Two tiers of authentication implemented using role
voters.
• PartiallyAuthenticatedVoter
Security
68. Symfony @ OpenSky
• Two tiers of authentication implemented using role
voters.
• PartiallyAuthenticatedVoter
• AuthenticatedRecentlyVoter
Security
69. Symfony @ OpenSky
• Two tiers of authentication implemented using role
voters.
• PartiallyAuthenticatedVoter
• AuthenticatedRecentlyVoter
• When a user is recently authenticated, they are logged
in to 2nd tier of authentication and have access to
account, checkout, etc. After 15 minutes of inactivity,
user is logged out of 2nd tier.
Security
70. Symfony @ OpenSky
• Two tiers of authentication implemented using role
voters.
• PartiallyAuthenticatedVoter
• AuthenticatedRecentlyVoter
• When a user is recently authenticated, they are logged
in to 2nd tier of authentication and have access to
account, checkout, etc. After 15 minutes of inactivity,
user is logged out of 2nd tier.
• Admin integrates with LDAP (using zendframework/
zend-ldap)
Security
71. Symfony @ OpenSky
• Two tiers of authentication implemented using role
voters.
• PartiallyAuthenticatedVoter
• AuthenticatedRecentlyVoter
• When a user is recently authenticated, they are logged
in to 2nd tier of authentication and have access to
account, checkout, etc. After 15 minutes of inactivity,
user is logged out of 2nd tier.
• Admin integrates with LDAP (using zendframework/
zend-ldap)
• Multiple firewalls/authentication providers for consumer
facing, admin and api.
Security
73. Symfony @ OpenSky
• Parameters defined in config/parameters.yml
• Override stored in MongoDB
Runtime Settings/Parameters
74. Symfony @ OpenSky
• Parameters defined in config/parameters.yml
• Override stored in MongoDB
• Parameters can have start & end datetime
Runtime Settings/Parameters
75. Symfony @ OpenSky
• Parameters defined in config/parameters.yml
• Override stored in MongoDB
• Parameters can have start & end datetime
• All parameters stored in cache
Runtime Settings/Parameters
76. Symfony @ OpenSky
• Parameters defined in config/parameters.yml
• Override stored in MongoDB
• Parameters can have start & end datetime
• All parameters stored in cache
• When parameter value is changed, cache is
updated and queue workers restarted
Runtime Settings/Parameters
77. Symfony @ OpenSky
• Parameters defined in config/parameters.yml
• Override stored in MongoDB
• Parameters can have start & end datetime
• All parameters stored in cache
• When parameter value is changed, cache is
updated and queue workers restarted
• Allows for configuration changes without
deployment
Runtime Settings/Parameters
78. Symfony @ OpenSky
• Parameters defined in config/parameters.yml
• Override stored in MongoDB
• Parameters can have start & end datetime
• All parameters stored in cache
• When parameter value is changed, cache is
updated and queue workers restarted
• Allows for configuration changes without
deployment
• Heavily used for feature flags and enable/
disable of new features
Runtime Settings/Parameters
83. Symfony @ OpenSky
• Perform tasks after the response has been sent
to client
Event kernel.terminate
84. Symfony @ OpenSky
• Perform tasks after the response has been sent
to client
• Store Request Log in MongoDB Collection
Event kernel.terminate
85. Symfony @ OpenSky
• Perform tasks after the response has been sent
to client
• Store Request Log in MongoDB Collection
• Analyze request for further actions
Event kernel.terminate
86. Symfony @ OpenSky
• Perform tasks after the response has been sent
to client
• Store Request Log in MongoDB Collection
• Analyze request for further actions
• Only effective with FastCGI
Event kernel.terminate
114. Symfony @ OpenSky
• A/B Split Testing using Optimizely for
testing UI/UX changes
• Send small % of traffic to new features
• GoogleAnalytics tracking conversion rate
The Frontend
117. Symfony @ OpenSky
• pr-nightmare
• Runs on all commits and pull requests
Testing
118. Symfony @ OpenSky
• pr-nightmare
• Runs on all commits and pull requests
• Uses PHPChunkit to run 30 minutes of tests in 3
minutes by running groups of tests in parallel
across multiple servers.
https://github.com/jwage/phpchunkit
Testing
119. Symfony @ OpenSky
• pr-nightmare
• Runs on all commits and pull requests
• Uses PHPChunkit to run 30 minutes of tests in 3
minutes by running groups of tests in parallel
across multiple servers.
https://github.com/jwage/phpchunkit
• Selenium for browser functional testing
Testing
121. Symfony @ OpenSky
• ‘@app admin’ annotation to indicate web test
cases which app/kernel to boot up
Testing with PHPUnit
122. Symfony @ OpenSky
• ‘@app admin’ annotation to indicate web test
cases which app/kernel to boot up
• AutoDataCleanupListener keeps tracks of what
collections and tables were inserted to in a test
and cleans up afterwards. This used to be
explicitly required by the developer but happens
automatically now
Testing with PHPUnit
123. Symfony @ OpenSky
• ‘@app admin’ annotation to indicate web test
cases which app/kernel to boot up
• AutoDataCleanupListener keeps tracks of what
collections and tables were inserted to in a test
and cleans up afterwards. This used to be
explicitly required by the developer but happens
automatically now
• AbstractTwigLintTest: Twig linting. Loads up all twig
files to make sure there are no parse errors
Testing with PHPUnit
124. Symfony @ OpenSky
• ‘@app admin’ annotation to indicate web test
cases which app/kernel to boot up
• AutoDataCleanupListener keeps tracks of what
collections and tables were inserted to in a test
and cleans up afterwards. This used to be
explicitly required by the developer but happens
automatically now
• AbstractTwigLintTest: Twig linting. Loads up all twig
files to make sure there are no parse errors
• Generating unit tests from class definitions
Testing with PHPUnit
125. Symfony @ OpenSky
• ‘@app admin’ annotation to indicate web test
cases which app/kernel to boot up
• AutoDataCleanupListener keeps tracks of what
collections and tables were inserted to in a test
and cleans up afterwards. This used to be
explicitly required by the developer but happens
automatically now
• AbstractTwigLintTest: Twig linting. Loads up all twig
files to make sure there are no parse errors
• Generating unit tests from class definitions
• PHPUnit watch command
Testing with PHPUnit
136. Symfony @ OpenSky
Deployment
• Use Customized Fabric
• Manually started from Jenkins
• First build task
• Merge feature/fix branch into
master
• Create Tag
137. Symfony @ OpenSky
• Final build task
• git checkout
• composer install --no-dev —no-scripts
• Build front controller
• Run gulp, requires, less, sass
• Warmup cache for each app
• Assetic dump
• Build artifacts file & distribute it to
servers
• Run MySQL & MongoDB Migrations
• Rotate logs
• Restart php-fpm
• Reload Varnish and nginx
• Restart queue workers
Deployment
• Use Customized Fabric
• Manually started from Jenkins
• First build task
• Merge feature/fix branch into
master
• Create Tag