 One second. OK, hello everybody. Apologize for, I need to apologize for my voice. I've been to Bangkok and I got a, like a sore throat, some kind of infection. It's over now, but you may not hear me well. So just shut out. Oh, there's a mic. OK. So that might be easier. So in this talk, I want to tell you a story which we kind of engage with one of our customers. And this story is basically describing what we've done and why we've done it. So the title says it's a good quality code with Drupal 7. But you could actually apply it to any framework or any software or any CMS or any platform you want. It's mainly about writing good code with some examples using symphony components as well in Drupal 7. I know that Drupal 8 came out a couple of months ago and everybody's already buzzing to use Drupal 8. But in real life, we have still many projects which we have to maintain in Drupal 7. So this idea I'm going to present here may give you some tips how to start doing things in the right way, but still with Drupal 7 and then migrate easily to Drupal 8. First words about me. I work for Sensual Apps UK. Sensual Apps is a company behind the symphony framework. You can find me on Twitter as SuperMarek. I work for the company, which is actually a group of companies. It's important. In Vika Group, as a group, has three companies inside. Session, which is a Magento company delivering Magento products, solutions, then Sensual Apps, which looks after symphony, and ICOS, which is a Drupal CMS little agency. So we work as kind of like a shared resources team. So the fact I'm working for Sensual Apps doesn't mean I won't be involved in the Drupal project or Magento project. So we had a customer who came to us and said, we have a mission, a mission for you. A mission for us was that it was a Magento platform they kept using for years. But they were kind of strapped with cash. So they didn't want to upgrade. They didn't want to patch it much. They just wanted to leave it as it is. That was an old version of Magento. And they were using it as a CMS. It was a business-to-business customer. So they didn't pay too much attention to how the content for customers looked like. But at some point, they realized that actually the way they present the data, information to the customers, even though they're business customers, it's actually important. So they came up to us with a challenge. They wanted to have a proper CMS, but they didn't want to spend too much money. So typical scenario, they want all the features, but they have no money, right? So if you know Magento, Magento is a great e-commerce platform, but it's not a good CMS platform. And what they wanted, they wanted to manage the corporate websites, but they also wanted to manage to be able to build macro sites, not necessarily tightly coupled to what they're doing right now. So the broad of uses they wanted was quite big. So we decided, we suggested that the best solution for them would be Drupal. At the time, that was Drupal 7. Drupal 8 was not out yet. And our in-house Drupal team said Drupal 8 beta was not ready yet. But why did we decided to go for Drupal? It's open source, CMS, it's there, it's done. All the features they wanted, most of them, they were already out of the box. So the only cost they were actually paying was to configure the platform for them and just give it to them so they can work with it by themselves. They were happy to look after timing that by themselves, building micro sites by themselves. They just wanted to have a platform which would deliver all those features. At the same time, they wanted to have some kind of connections with Magento. And they operated on a very limited budget. The project eventually went live, but thanks to the customer, it got delayed by almost a year. That always comes when you have a limited budget. And when you say, yeah, right now I need to stop. And let's wait a couple of months till we get a budget to carry on. But anyway, project went live. So whatever I'm going to talk about in here, it's live and it's working. So we decided to build kind of like a connection between Magento and Drupal. And this connection was being managed by Composer. Do you know what's Composer? It's like an awesome tool to manage dependencies. Drupal 7 doesn't use it. But you can use it with Drupal 7. We use symphonic components to build mini tasks, which I explain a bit later. So next step. So before I explain you what we've done, let me talk a bit about clean code. So this is the thing which we love to do at my company. We pride ourselves in being software engineers where we write beautiful code we can be proud of. So at what point we can decide that code is beautiful and that we're proud of this code? So Eric Evans, the guy who's created a brilliant book about the domain-driven design, said something like that in order to create a good software, you have to know what the software is all about. So basically in order to write a good software, you have to learn what the business you're talking to is all about. If you want to build a software which represents what business is doing, it's great if you can translate that directly to your code. So how do we learn about those processes? So first of all, when you talk about projects, you have to find stakeholders, owners of the project who know what exactly needs to be delivered. The bigger project, the bigger company you deal with, the more people they have. So it becomes difficult to pinpoint those people you want to talk to. Most of the time you get Project Manager who doesn't care and just works as a proxy between business and developers without paying attention. That's the problem. Sometimes you have stakeholders who don't care who just say, this needs to be done on this date and I don't care. So these are difficulties you may face. The best way to get stakeholders involved is just to bring them to all discussions the developers have. It doesn't have to be all stakeholders. Just find the person who's the most interested in the project and involve them as much as you can. So when you talk to the stakeholder, ask them to describe using examples how the system should behave. Ask them to describe using simple stories how they want the system to work. And the examples should be, I'll get to examples, and ask them to describe the behavior of how the system should behave. And by engaging the stakeholder in doing so, they will start using language you want to use as well. You start building a vocabulary of the same words. So when you talk about product, you know what product means. It's not a product item or it's not a catalog item. It's not an inventory item for the stakeholder. It's a product. I've been in a couple of projects where in one of my projects where we did a big mess up with namings was selling courses. So the customers had on the website a list of courses. And each course had events, which you can book into. So let's say there was a course, Symphony Training, which had a couple of dates. Those dates were events. In a code base, we call them course types and courses. That led to many confusions. Because when we talk to the customer, and then we look at the code, or even thinking about that, we were thinking about two different things. Once you have a common language, ubiquitous language, ubiquitous language is like a common language you have between all the parties. And it's good to keep that language, the same language, between all the parts involved in building your application. So if you talk to developer tester analysis to developer or domain expert or stakeholder to developer, all parties should use exactly the same language. And the same language should land in your code, acceptance criteria, specs, and documentation. So avoid a translation cost as much as possible. And if you talk to stakeholder and you ask them for examples, ask them to give examples in a form like that, in a form of a feature with a scenario written ideally by the domain expert or written by the business analyst with the domain expert or stakeholder. And this scenario is quite simple. As you see, have you ever used BHAT? So some of those scenarios people use to describe how the browser should be clickable. So whenever you write those examples, don't try to write scenarios when I click that button and enter this field and fill that field and enter this button. And I go to another page. That's not a behavior. Behavior is like, given I'm visiting a product page for the first time, and I got prompted to locate my nearest depot, when I enter my postcode, then I should see my local depot information. It's as little concrete information about the browser behavior as possible. Sometimes you cannot avoid it in some stories. So when you have this little example, you can extract keywords. You have an actor of your story. So in order to see correct prices as a visitor, I need to be able to locate nearest depot. So visitor is your actor. Then you have some actions. Those actions you can translate directly to your code. So in our example, basically customers were business to business customers. They were based all around UK. Whenever they were logging or coming to the website, they wanted to see the local warehouse prices. And unfortunately, the prices were not the same across the UK, so they had to enter the location. They were only allowed to use one warehouse, which they belonged to, so they could see the prices. And that was the simple story which we had to implement. And that translated into a code base, into a code. So we built an interface depot locator, which basically describes a system service, which has just one method, locate depot by postcode, and returns depot information. So having interface is a good idea, because for testing, you can implement a test adapter for your final code base. You can implement final solution. This is an example how we then implemented Magento depot locator. So this was basically calling Magento service to map the postcode back to depot in our system. It was behind the scenes. It was calling Magento API, retrieving JSON, and just retrieving. But because this was hidden by the interface, that was easy to stab in a test. One interface describes communication. Interfaces, the more interfaces you use, the better. The slimmer interfaces are the better. They describe how your objects communicate with each other. Robert C. Martin, Uncle Bob, said that high level modules should not depend on a lower level implementation. What that means is when you think about your application, your application has different levels of engagement, let's say. You have user interface, which is being interacted by user, then you have your application, you have your domain, and then you have your infrastructure. So whatever your domain does, your domain should not know about the user interface. So you shouldn't have in your domain, you shouldn't use language, which is about clicking, feeling fields, or something like that. It should be user interface agnostic, or even framework agnostic. So in our example, when we had interface, so our domain said that I want to communicate with the port location via this interface. So the infrastructure is the layer which implements that interface, and it's basically used by your domain or application. So it's basically something like that. You've got policy layer which uses some policy service, then it's implemented by mechanism layer, which may use some mechanism service which is provided by utility layer. So this is your interface, which can be implemented by many other, many different objects. You can replace them for testing, for stabbing. So if you look at this, the higher level you are, you should only talk to the lower level of abstraction. So if you look at the Drupal in Magento in here, from our perspective, from this exercise perspective, Drupal was the user interface. Magento was the infrastructure. Our code, what we've written, was the application domain. Drupal was just displaying what was coming from Magento. Clean architecture. So it's basically a similar view, but just different take on it, what I said. So you have entity use cases, controllers, and web. I'll just skip it quickly. Another take on it is a hexagonal architecture, which is basically the same what I showed you before with a layer architecture, but slightly different view. Your application domain is in the center, and everything what doesn't belong to your application domain should be hidden behind the port or interface. So in our example is Acme Deput Locator Interface. It's our port. Magento Deput Locator is our adapter. OK, why clean architecture? You end up writing less code. And the code is less complex. If you start doing those exercises, it may sounds like you keep writing more classes. You keep creating more objects. And it may sounds like it's not really less complex. It is because your classes objects are going to be simpler, smaller, usually like two or three lines maximum, replacing one long 60 line method. And if you have to debug it, it's much easier to unit test your free line object than to debug your 60 line spaghetti code. They can be decoupled. After this exercise, we had a Magento guys trying to use modern tools, PHP spec, to write a module for Magento. So what they did, they created a mage spec extension to PHP spec so they can use all the goodies from Magento inside PHP spec. They created a module and they realized, actually, we don't need all those add-ons from Magento because our module is already decoupled. So they removed that and just made it work with a clean PHP spec. And then they realized that, actually, this is not related to Magento. It works with my Magento project, but it could be released as an open source project. And other people could use it. So if you use the coupled code, you can reuse it with other frameworks as well. Whenever you need a framework, hide it behind the interface. So it doesn't work if you're just adding pages in Drupal because you're just using the built-in functionality. But if you have to write custom code, then it makes sense to do it this way. And Zen Framework 3 is just around the corner. Maybe it's going to be better than Symphony. And next year, everybody will be moving to Zen Framework 3. It might happen. Raiseable components, easy to test. Who's writing tests? Right. The good pro tip, write tests before you write code. It seems impossible at first. Once you start doing that and you get used to it, you won't be able to go back. Easy to maintain, easy to change. The biggest problem with legacy applications is that we are afraid to change them, right? So if you have a nice, clean code, it's a lot easier to change. Good books to read. So we have a clean code and a clean code there by Uncle Bob, Robert C. Martin. These are highly recommended. Domain-driven design. After you go through those two, then it might be a bit boring, the domain-driven design at times. But it's a really, really good knowledge. OK, so the impossible. How we did it. First, we started with dependence injection. So long story short, we basically created our own micro framework using symphony components which sit between Magento and Drupal. And this is how you do it most of the time. There are people like Tyler Otwell who create micro frameworks and then give them fancy names and then try to sell them. But you don't have to buy anything. You don't have to follow someone's solutions. You can do it by yourself every time. Symphony is so flexible that you can just cherry pick which component you want and you can do it. With Zend Framework 3, you've got exactly the same. They have now fully decoupled components. If you want to use symphony dependence injection with a router from Zend Framework, there is nothing which stops you from doing that now. So dependence injection standardized way of loading dependencies, a pretty cool feature. So we created our kernel, which this container builder is a standard class from the dependence injection container component in symphony. Why we started like that? We wanted this to be run without Drupal. Why? I get to that in a second. Container loads configuration from YAML or XML file. It's very handy. Services loaded from XML. We use XML because some of our colleagues insist of using PHP Storm. Some people find it useful. And PHP Storm can autocomplete your service names from XML files. So it's useful. You can register extensions, application extensions to modify parameters from the configuration. We extracted it into a method, private method. So we have a boot method with just builds container. And the build container just returns that. Yeah, then we added caching. So we only need to build the whole container and all dependency injection once when we install application. We don't need to rebuild it every time we want to use it, right? Although if we end up, we actually want to rebuild it. So our parameters.yaml had all the configuration for Database, Soap Client, which had to talk to Magento, some configuration for the product operator. We created services XML. I would prefer YAML, but some people insist on XML. So let's build first console command. Our first command was built to import products from Magento and then save them to a source. So short story, why? The problem with Magento was that it was very slow. That's standard for Magento, right? So we didn't want to hit Magento every time someone was searching for products on a Drupal site. So we wanted to keep a cache of information. So this was a command line tool which would import products from Magento and just dump them somewhere. So it takes interfaces if you wondered what were product imported and product persisted. These are just interfaces. So in tests, we can replace them with something which is useful for tests, mock them, or whatever. So yeah, simple command. That's an import and persist command. That's a symphony command, which you can run from the command line. So you have to register it. And the import and persist handler is injected as a service in here. And it displays simple execution time. And our console file, very simple. We have an autoloader from vendors, from Composer. Use our kernel we just built. Use console application from symphony. Boot our kernel, register application, register commands, and run. And this is 10 minutes. And symphony sorted out as a couple of commands. All the infrastructure for microservice app was done. OK, so persisting products. So in the first attempt, we decided to use XML feed importer. It's a module in Drupal. The purpose of this module is it can take a feed, import it, and leave it, right? Problem was, it didn't work. So in our case, we decided to, in our product persister, we just dump XML product information to the file. And feed importer in Drupal should take it on. It should import all product information. Thing was, it had to be run every day at around 5 AM, because the data, the master record for products was still in Magento. But Drupal site had to be updated every day. And customer decided that one day delay in getting updates is enough, is OK. So yeah, fetching that data from Magento was quick. Then we can import it to Drupal. Why didn't it work? Feed imported was very slow. And it was very slow. 10,000 products imported in four and a half hours. Whatever way we wanted to configure it, it was just not working. Scheduling import with Elysia Kron worked and then didn't work. And then started going three times in a row. And it was resources hungry. It was, it basically didn't work for us. So we decided to save directly to the database. And yeah, so we just needed to create a new database product per system. Instead of XML, now we write to the database. We just, yeah, repository is interfacing here and just saves products. So why direct database write? Drupal says that the data structure would be always the same. We didn't want to keep history of the changes in products. We just, the only thing which mattered was the product information was up to date. So it was fine. Then it was fast. 40,000 products imported within less than two minutes. And we didn't have doubles. We didn't have problems with Elysia Kron. So we didn't have a problems with normal problems with feed importer and normal problems with resources. So we were happy. Yeah, so our Composer JSON file at the end of the exercise. Very simple. We installed Drush using Composer. You can install it globally. But we have a preference to have a project version tools rather than global tools. Symphony console, config file system, doctrine double. We didn't go for the full doctrine bridge. Just database abstraction layer was enough to perform saves. Yeah. So now we wanted to make one of the services to be available in Drupal. But the service was written as a decoupled component used with Symphony application. So we created a click called container.php, which would load the Composer autoloader, instantiate our kernel, boot it, and set it as a viable DA container. It's a nasty hack. But if you deal with nasty applications, this is the way to do it. So thanks to that, we could have the same database configuration across both applications. Because in our database.php, we just take the database configuration from our DA container. Perfect for our deployment. Yeah, remember that container had to be included before database. Then we wrote our depot locator module, which is very simple. Takes postcode, then takes container, takes our service, depot locator, dumps postcode that gets depot to array, sets a cookie, returns. So this depot locator thing is here. It's a decoupled service, which we just inject here, use, and forget about it. And our depot locator knows nothing about Drupal. And you could use it with any framework you wanted. Yeah, project folder structure. This is the thing people usually ask, how we manage that. Drupal has its own folder structure. So Drupal was in a folder called public, as it's always, and messes with the files and stuff. So our spec files, which we use PHP spec to write unit tests, was in the spec folder. Features, we had features, are in the features folder. Sources, when the source is, vendor is where the symphonic components went. App, application, bin, some random stuff. Composer, build. Pretty simple. OK, let's rack up Drupal. Whenever I speak about Drupal, people say, oh, you symphonic guy, you don't like Drupal. I love Drupal. Drupal is great. And don't let people tell you that Drupal isn't great. Every project, it's like Drupal is not the greatest project. It's the code, especially in Drupal 7. It's not the greatest. But if you don't need to write any code for it, or if you just want to use it, it's great. It works. It does the job. If I wanted to build my blog right now, would I go back to the keyboard and write my blog from the scratch? No, I would use WordPress or something like that. Use the tools which are available for you. Don't try to reinvent the wheel. Unless you're really good and you really want to impress people and you want to build a new awesome platform and you have an idea for it, then yes, go for it. Symphonic components. Cherry pick what you want. Just use what you want. Symphonic, full symphonic framework is huge. It's pre-configured for you. If you build a huge application from the scratch, it's awesome. But if you want something small, something tiny, better, just pick a couple of components you need, configure it, and that's it. Decoupled code. Highly recommended. Unit tests at the unit code level. Highly recommended. Write tools. That's all. Any questions? So you said you decided not to use the Drupal Feet module because it's too slow for your purpose. And you create some symphony code to write these nodes into Drupal Directly. Were you able to call the Drupal functions within your symphony code, or were you writing directly to the database tables? So we're writing directly to the database. So then I have a bit of an issue with that type of architecture because Drupal has a function that would save you a populated object and then you save it. And then Drupal would then do the inserts into the table. And oftentimes, unless you know intimately which tables get updated when you're creating a new product on Drupal, how are you certain that you're getting all the right tables through your custom inserts? Well, first of all, we have an in-house Drupal knowledge. And so we consulted that with them. And that was their recommendation. We didn't want to use Drupal functions because that would require to spin up the Drupal stack. And that would slow down the import massively. So that's why we went for that this way. We had a clear way to identify which products we basically created a field to identify them. And knowing the content structure, the database structure for product information, we were able to update the product information basically in seconds. I know it's a nasty hack. But given the requirements, that was the way we had to do it. Just on a side note, I'm actually using speeds extensively now. I'm able to process 1,000 XML files in about three or four minutes, I think on a local. Yeah, so you might want to revisit that if you ever get a chance. So it could be other. So we found that it's not necessarily the import process that's slowing it down. But in our particular case, it was connected to a third party module that was delaying the node save that was slowing the process down. So once you disable that module, then it's super fast. So yeah, so sometimes these kind of things may look slow. But yeah, no. Probably in our case, it was something similar. The problem was that the customer had a limited budget. So we basically, to deliver this, we had free sprints. So six weeks, including building the whole. Yeah, I understand how it goes sometimes with customers. Thanks. More questions?