I'm learning my ways with Symfony2 while building a small eshop for a family-run wine importer. Slowly I'm gaining understanding of the Symfony2 concept but while moving on to building a shopping cart, I'm not quite sure what would be the correct (at least according to the Sf2 standards) way to implement this.
What I'm looking for is a simple Basket storing BasketItems and their amount in sessions and later use this data in the checkout. Now before I go and glue this somehow together, I'd like to hear an advice on how this should be done and separated correctly.
So far, I've created the two as entities and now I'm not sure if I should put the logic directly in the Entity classes or Repositories or where preferably?
I hope this is not a too wide question. I'm well aware of the fact that a truly robust shopping cart implementation is no small job.
On a sidenote, are there already some proven and working shopping baskets for Symfony2 around?
The general rule when creating Entities and Repositories is that an Entity class should contain the logic needed to operate on a single Entity, and Repository classes contain the logic needed to operate on groups of Entities.
A simple example:
If you wanted to convert the price of an item from dollars to euros, you would have BasketItem::convertCurrencyTo($currencyType). This is code that operates on a single Entity.
If you wanted to find all BasketItems whose price is between $10 and $20, you would use BasketItemRepository::findByPriceRange($min, $max). This is code that operates on a group of Entities.
IMO, things can get a little confusing when dealing with Entities that have a one-to-many or many-to-many relationship. I would imagine that one Basket has many BasketItems, and one User has one Basket. Therefore, I think it's reasonable to assume that in order to fetch the BasketItems a User is interested in, you could do something like this:
$user->getBasket()->getBasketItems().
Note that there is no Repository being used in this example because it's a very basic query. If you needed to do something more specific, like find the BasketItems a User is interested in that happen to be on sale, you could use something like BasketItemRepository::findWhereOnSaleForBasket($BasketId). There's nothing stopping you from adding a similar method directly to the User or Basket classes, but I feel that because your query's main 'target' is a BasketItem, it belongs in that Entity's Repository class.
You mention that implementing a shopping cart is no small task, and you're right. But the questions you're asking aren't really limited to Symfony2 (in this case it's more about Doctrine 2), so I think it might be helpful if you check out other open source shopping carts and study what they're doing. I think the only impact Symfony2 would really have is forcing you to decide how to split up your code within bundles.
Hope this helps!
There is an ecommerce bundle available called Vespolina which also provides a cart.
Related
Diving into the world of DDD is something of a whirlwind for me. While I've done a lot of research I'm struggling to change my thought process.
So, I have a package and a products entity. A package cannot function without products; vice-versa. The problem comes in when I'm needing to get the products belonging to a package (Note: the package is customisable, this means the products belonging to the package could be different the next time round). It seems that this belongs on neither entity, furthermore applying this to the package or product entity would make them tightly coupled.
I must stress that I'm using the word association because I'm trying to figure this out on a domain level rather than infrastructure.
A little thinking has led me to the following thoughts:
Make Packages an Aggregate root & the products to be a sibling of the
Package entity. However I believe that both of the Package & Products
are entities due to them been uniquely identified.
Create a domain service that would take the package ID & map to the
infrastructure layer to find the associated products. However, this
seems rather long winded for a single look up.
It would be great if someone could turn me sane again! Thanks in advance!
Ubiquitious Language
DDD is all about language - the key here is to listen to your domain experts talk about products and packages - how do they think about them? And about the processes involved in working with them?
When they create a package, do they really think to themselves "I must define products with this package", or do they think "I'll setup a package, and then link a few products to it" - if the latter, although it may on first blush feel like a package can't exist without products, notice the subtle implication in the timing - that the package can exist without products, because they expect to link products to it as a second step.
Given that, you might model the link as a completely independent aggregate with a single entity as the root, e.g. PackageProduct (or better yet, some term your domain experts use to define the assocation) and simply create new instances of this aggregate when a product is linked to a package. This entity would have a PackageId and a ProductId on it.
However, if there are business rules, e.g. a Package can only have one product of a given type, or at most 5 products, then make the PackageProduct entity an entity within the Package aggregate, which has Package as the aggregate root. The PackageProduct would have a reference to the Package and a property of the ProductId. See below for some terminology clarification.
Entities vs Aggregates
Based on your question, it seems there might be some confusion about terminology. In DDD, we have:
Entities:
Have an identity that outlasts any given property
Has a mutable state
Generally, modelling business processes is all about working out how to mutate the state of entities
Aggregate
A group of entities over which invariants must be enforced
invariants are business rules that MUST hold at all times
A single entity is always nominated as the 'aggregate root'
Other entities can only refer to the aggregate by the root
When modelling a business process in order to mutate state, the aggregate is the boundary within which the states of all entities must be consistent with the invariants.
Outside the aggregate boundary, other entities may get updated asynchronously - aiming for eventual consistency
Domain Service
A service that contains business logic that doesn't belong in a single entity
It is generally not just a wrapper around a piece of infrastructure
See https://lostechies.com/jimmybogard/2008/05/21/entities-value-objects-aggregates-and-roots/ for more info.
Read vs Write Operations
The problem comes in when I'm needing to get the products belonging to a package
This sounds a lot like it might be in order to support a UI or a report? In which case - don't stress about the entity model. The entity model is there to ensure the business rules hold while users are trying to modify the state of the system. When doing a read operation, there is no need to modify the state, so you can bypass the model. Define a query that projects your data store onto a DTO and tailor the the projection to suite the needs of the UI or report.
The problem comes in when I'm needing to get the products belonging to a package (Note: the package is customisable, this means the products belonging to the package could be different the next time round).
This sounds like a query. It often helps to separate the modelling of commands (things that can alter your domain model) and queries.
Let's say I have a group of database tables (customer, product, category, customer_products, product_categories, customer_categories) and classes corresponding to each of them (basic entity classes for the basic entity tables, and classes like CustomerProductLinker for the foreign key tables). The "linker" classes have methods like addXToY, removeXFromY, getXsOfY, and getYsOfX.
The problem comes from the additional business requirements that 1) adding a Customer to a Product should add that Customer to each of the Product's Categories, and 2) adding a Product to a Category should add each of that Product's Customers to the Category. With the current class system, that creates a dependency cycle between CustomerProductLinker and ProductCategoryLinker.
I do not know how to solve this. Combining all the "linkers" into one big "SuperLinker" class seems very wrong. And keeping the linker object instantiation inside only the methods that need them rather than passing the linkers in via constructor injection seems worse (though it would technically solve the cycle).
I doubt this system of linker classes is the best way to handle this kind of setup, but I'm not experienced enough to know what could be done instead. Unfortunately I can't use an ORM, because the tables are not all stored on the same server. So... What else can be done?
I need help with something I can’t get my head wrapped around regarding the Repository and Service/Use-case pattern (part of DDD design) I want to implement in my next (Laravel PHP) project.
All seems clear. Just one part of DDD that is confusing is the data structures from repositories. People seem to choose data structures the Repository should return (arrays or entities) but it all has disadvantages. One of which is performance looking at my experiences in the past. And one is which you don’t have interfaces for simple data structures (array or simple object attributes).
I’ll start with explaining the experience I have with a previous project. This project had flaws but some good strengths I learned from and like to see in my new project but with solving some design mistakes.
Previous experience
In the past I’ve build a website that was API Centric using the Kohana framework and Doctrine 2 ORM (data mapper pattern). The flow looked like this:
Website controller → API client (HMVC calls) → API controller → Custom Repository → Doctrine 2 ORM native Repository/Entity-manager
My custom Repository returned plain arrays using Doctrine2 DQL. Doctrine2 recommends array result data for read only operations. And yes, it made my site nice and light. The API controller just converted the array data to JSON. Simple as that.
In the past my company created projects relying fully on loaded Doctrine2 entities and it’s something we regretted due to performance.
My REST API supported queries like
/api/users?include_latest_adverts=2&include_location=true
on the users resource. The API controller passed include_location to the repository which directly included the location relation. The controller read latest_adverts=2 and called the adverts repository to get the latest 2 adverts of each user. Arrays were returned.
For example first user array:
[
name
avatar
adverts [
advert 1 [
name
price
]
advert 2 [
….
]
]
]
This proved to be very successful. My whole website was using the API. It would be very easy to to add a new client because the API was perfectly in production already using oauth. The whole website runs on it.
But this design had flaws too. My controller still contained A LOT of logic for validation, mailing, params or filters like has_adverts=true to get users with adverts only. It would mean that if I created a new port, like a total new CLI interface, I would have to duplicate alot of these controllers due to all the validation etc. But no duplication if I would create a new client. So at least one problem was solved :-)
My admin panels were completely coupled to the doctrine2 repository/entity-manager to speed up development (sort of). Why? Because my API had fat controllers with special functionality for the website only (special validation, mailing for registering etc). I would have to redo work or refactor a lot. So decided to use the Entities directly to still have some sort clear way of writing code instead of rewriting all my API controllers and move them to Services (for site & admin) for instance. Time was an issue in fixing my design mistakes.
For my next project I want all code to go through my own custom repositories and services. One flow for good separation.
New project (using DDD ideas) and dilemma with data structures
While I like the idea of being API centric, I don’t want my next project to be API centric in core because I think the same functionality should be available without the HTTP protocol in between. I want to design the core using DDD ideas.
But I liked the idea using a layer that just talked as a API and returns simple arrays. The perfect base for any new port, including my own frontend. My idea is to consider my Service classes as the API interface (return the array data), do the validation etc. I could have Services specially for the website (registering) and plain services used by the Admin or background processes. In some admin cases a Service would not be required anyway for simple CRUD editing, I could just use Repositories directly. Controllers would be very thin. With this creating a real REST API would just be a matter to create new controllers using the same Services my frontend controller classes do.
For internal logic like business rules it would be useful to have Entities (clear interfaces) instead of arrays from repositories. This way I could benefit from defining some methods that did some logic based on attributes. BUT If I would be using Doctrine2 and my repositories would always return Entities my application would suffer a big performance hit!!
One data structure ensures performance but no clear interfaces, the other ensures clear interfaces but bad performance when using a Data Pattern pattern like Doctrine 2 (now or in the future). Also I could end up with two data types which would be confusing.
I was thinking something similar to this flow:
Controller (thin) → UserService (incl. validation) → UserRepository (just storage) → Eloquent ORM
Why Eloquent instead of Doctrine2? Because I want to stick a bit to what’s common within the Laravel framework and community. So I could benefit from third party modules, for example to generate admin interfaces or similar based on models (bypassing my DDD rules). Other than using third party modules, I would design my core stuff so switching should always be easy and not affect data structure choices or performance.
Eloquent is an activerecord pattern. So I would be tempted to convert this data to POPO’s like Doctrine2 entities are. But nope... as said above, with doctrine2 real models would make the system very fat. So I fall back to simple arrays again. Knowing this would work for both and any other implementation in the future.
But it feels bad always rely on arrays. Especially when creating internal business rules. A developer would have to guess values on arrays, have no autocompletion in his IDE, could not have special methods like in Entity classes. But making two ways of dealing with data feels bad too. Or I am just too perfectionist ;) I want ONE clear data structure for all!
Building interfaces and POPO’s would mean a lot of duplicate work. I would need to convert an Eloquent model (just a table mapper, not entity) to an entity object implementing this interface. All is extra work. And eventually my last layer would be just like a API, thus converting it to arrays again. Which is extra work too. Arrays seem the deal again.
It seemed so easy reading up into DDD and Hexagonal. It seems so logic! But in reality I struggle with this one simple issue trying to stick to OOP principles. I want to use arrays because it’s the only way to be 100% sure I am not depended on any model choice and querying choice from my ORM regarding performance etc and don't have duplicate work in converting to arrays for views or an API. But there's no clear contract on how a user array could look. I want to speed up my project using these patterns, not slow them down :-) So not an option to have many converters.
Now I read a lot of topics. One makes POPO’s & interfaces that conform proper entities like Doctrine2 could return, but with all the extra work for Eloquent. Switching to Doctrine2 should be fairly easy, but would impact performance so bad or one would need to convert Doctrine2 array data to these own entity interfaces. Others choose to return simple arrays.
One convinces people to use Doctrine2 instead of Eloquent, but they leave out the fact that Doctrine2 is heavy and you really need to use array results for read only operations.
We design repositories to be changeable right? Not because it’s “nice” by design only. So how could we rely on full Entities if it has such big impact on performance or duplicate work? Even when using Doctrine2 only (coupled) this same issue would arise due to its performance!
All ORM implementations would be able to return arrays, thus no duplicate work there. Good performance. But we miss clear contracts. And we don’t have interfaces for arrays or class attributes (as a workaround)... Ugh ;)
Do I just miss a missing block in our programming languages? Interfaces on simple data structures??
Is it wise to make all arrays and have advanced business logic talk to these arrays? Thus no classes with clear interfaces. Any precalculated data (normally would be returned by an Entity method) would be within an array key defined the Service class. if not wise, what’s the alternative considering all of the above?
I would really appreciate if someone with great experience in this “domain” considering performance, different ORM implementations, etc could tell me how he/she has dealt with this?
Thanks in advance!
I think what you are dealing with is something similiar I'm struggling with. The solution I'm thinking works best is:
Entities/Repositories
Use and pass around Entities always when performing Write operations (Creating things, Updating things, Deleting things, and complex combinations thereof).
Sometimes you may use Entities when doing Read operations (when you anticipate the Read might need to be used for a Write soon after...ie. ->findById is soon followed by ->save).
Anytime you are working with an Entity (whether it be Write or Read), the Repositories need to be the place to go. You should be able to tell new developers that they can only persist to the database through Entities and the Repository.
The Entities will have properties that represent some Domain Object (many times they represent a database table with the fields of a table, but not always). They will also contain the domain logic/rules with them (ie. validation, calculations) so they are not anemic. You may additionally have some domain services if your Entities need help interacting with other Entities (need to trigger other events), or you just need an additional place to handle some extra domain logic (perform Repository calls to check for some unique conditions).
Your Repositories will solely be for working with Entities. The Repositories could accept Entities and do some persistence work with them. Or they could accept just some parameters, and do some reading/fetching into full Entities.
Some Repositories will know how to save some Domain Objects that are more complex than others. Perhaps an Entity that has a property which contains a list of other Entities that need to be saved along side the main entity (you can dive deeper into learning about Aggregate roots if you want).
The interfaces to Repositories rest in your Domain layer, but not the actual implementations of those Repositories. That way you can have an Eloquent version or whatever.
Other Queries (Table Data Gateway)
These queries won't work with Entities. They'll just be accepting parameters and returning things like Arrays or POPO's (Plain Old PHP Objects).
Many times you will need to perform Reads that do not return nicely into a single Entity. These Reads are typically more for reporting (not for CRUD-like operations, like Reading a user into an edit form that is eventually submitted and saved). For example, you might have a report that is 200 rows of JOINed data. If you used the Repositiory and tried to return large deep objects (with all the relationships populated, or even lazy-loaded) then you are going to have performance issues. Instead, use the Table Data Gatway pattern. You are just displaying data and not really needing OOP power here. The outputted data could however contain ID's, which through the UI could be used to initiate calls to Repository persistence methods.
As you are developing your app, when you come across the need for a new Read/Report query, create a new method in some class somewhere in your Table Data Gatway folder. You may find you have already created a similar query, so see how you can consolidate the other query. Use some parameters if necessary to make the gateway method's queries more flexible in particular ways (ie. columns to select, sort order, pagination, etc.). Don't make your queries too flexible though, this is where query builders/ORMs go wrong! You need to constrain your queries to a certain extent to where if you need to replace them (perhaps a different database engine) then you can easily perceive what the allowed variations are and aren't. It's up to you to find the right balance between flexibility (so you have more DRY code) and constraints (so you can optimize/replace queries later).
You can create services in your Domain to handle receiving parameters, then passing them to the Table Data Gateway, and then receiving back arrays to do some more mutating on. This will keep your Domain logic in the domain (and out of the infrastructure/persistence layer of the Repository & Table Data Gateway).
Again, just like the Repository, use interfaces in your domain services so that the implementation details stay out of your Domain layer, and resides in the actual Table Data Gateway folder.
I'm using a self constructed database model. This model is constructed for a webshop application. this is how it looks like:
First of all I have a table for my products. This contains only general data like id and articlenr, for all of the product attributes (like name, price,etc) I have made seperate tables per type, so I have the following tables :
product_att_varchar
product_att_decimal
product_att_int
product_att_select
product_att_text
product_att_date
these tables are related by a relational table procuct_att_relational
My problem is the performance of this structure, if I want all the attributes of a specific product if have to use so much joins that it will slow down very much.
Does anyone have a solution for this???
Thanks
This model is called EAV (entity-attribute-value) and has its drawbacks and benefits.
Benefits are that it's very flexible and can be extended easily. It may be useful if you have very large number of very sparse attributes, the attributes cannot be predicted at design time (say, user-provided), or the attributes that are rarely used.
The drawbacks are performance and inability to index several attributes at the same time. However, if your database system allows indexed views (like SQL Server) or clustered storage of multiple tables (like Oracle), then by using these techniques performance can be improved.
However, storing all attributes in one record will still be faster.
I don't see any good reason to move those attributes out of the product table. It'd be one thing if you did it because you had some data that suggested a problem, but it looks like you thought "this will be better". Why did you do it this way right off the bat?
If you did it this way because it was generated for you, I'd recommend abandoning that generator.
People keep coming back to this model because they think it's "flexible". Well, it is I suppose, but that flexibility comes at a huge price: Every update and every query is slow and complex. Quassnoi mentions that if the attributes are sparse, i.e. most entity instances have only a small percentage of the possible attributes, this can save space. This is true, but the flip side is that if it is not sparse, this takes hugely more space, because now you have to store the attribute name or code for every attribute in addition to the value, plus you need to repeat some sort of key to identify the logical entity instance for every attribute.
The only time I can think of when this would be a good idea is if the list of attributes needs to be updatable on the fly, that is, a user needs to be able to decide to create a new attribute whenever he likes. But then what will the system do with this attribute? If you just want the user to be able to type it in and then later retrieve what he typed, easy enough. But will it affect processing in any way? Like, if the user decides to add a "clearance sale code", how will your program know how this affects the sale price? It could be done of course: You could have additional screens where the user enters data that somehow describes how each field affects pricing or re-ordering or whatever. But that would add yet more layers of complexity.
So my short answer is: Unless you have a very specialized requirement, don't do this. If you are trying to build a database describing items that you sell, with things like description and price and and quantity on hand, then create one table with fields like description and price and quantity on hand. Life is hard enough without going out of your way to make it harder.
I'm am designing my database/domain for an eCommerce application and I'm having a hard time figuring out how to store products.
The website will sell a wide range of products, pens, thongs, tattoos, umbrellas, everything. Each of these product will share a few common attributes, height, width, length, weight, etc but some products have special data. For example, pens have different ink colors and tips/lids and brochures can have different types of folds. So far I have thought up some 20+ extra attributes, but these attributes may only apply to 1% of products on the website.
So I am wondering if it is appropriate to implement a EAV model to handle the extra data. Keeping in mind that when customers are viewing the site in the frontend, there will be a filtering sidebar like on eBay and carsales.com.au. (So keeping in mind there will be a fair bit of querying)
I don't think it's practical to implement Class Table inheritance as the system needs to remain flexible. This is because, down the track we may have more attributes in the future with new types of products.
The other thing I have considered is using a NoSQL database (probably MongoDB) however I have little experience with these types of databases, will it even solve my problem?
Review of options:
Single products entity with lots of columns
Separate attributes entity (EAV)
Switch to schema-less persistence
I'm in the process of building a prototype with an attributes entity to see how flexible it is, and testing the performance and how out of control the querying gets.
EDIT: I am, of course, open to any other solutions.
Great question, but of course, there is no "one true way". As per #BenV, Magento does use the EAV model. My experience with it has been overwhelmingly positive, however it does trip up other users. Some considerations:
1. Performance.
EAV requires complex, multi-table joins to populate your object with the relevant attributes. That does incur a performance hit. However, that can be mitigated through careful caching (at all levels through the stack, including query caching) and the selective use of denormalization. Magento does allow administrators to select a denormalized model for categories and products where the number of SKUs warrants it (generally in the thousands). That in turn requires Observers that trigger re-indexing (always good!) and updates to the "flat" denormalized tables when product data changes. That can also be scheduled or manually triggered with a prompt to the administrator.
2. 3rd Party User Complexity
If you ever plan to make this application available to other users, many will find EAV too complex and you'll end up dealing with a lot of bleating and uninformed abuse on the user forums (ref Magento!!).
3. Future extensibility and plugin architecture.
There is no doubt that the EAV model really comes into it's own when extensibility is a factor. It is very simple to add new attributes into the model while minimizing the risk of breaking existing ORM and controller code.
4. Changes in datatype
EAV does make it a little harder to alter attribute datatypes. If your initial design calls for a particular attribute datatype that changes in future (say int to varchar), it means that you will have to migrate all the records for that attribute to the corresponding table that matches the new datatype. Of course, purists would suggest that you get the design right first time, but reality does intrude sometimes!
5. Manual product imports
One thing that EAV makes almost impossible is importing products (or other entities) into the database using SQL and/or phpMyAdmin-style CSV/XML. You'll need to write an Importer module that accepts the structured data and passes it through the application's Model layer to persist it to the database. That does add to your complexity.
The open source shopping cart Magento allows custom attributes for their products using an EAV design. You can check out their database schema here.
I would suggest you to look closer on Doctrine 2 ORM with OXM plugin for it (https://github.com/doctrine/oxm). It will solve your problem with different attributes. Of course you will be required to build indexes for searchable custom attributes, but I don't think it will be a problem :)
If you don't care about number of community members, then you can use MongoDB as well.