
Shop't
Shop platform
Well, it's been a while. Refactored a lot to prepare to migrate to .net core.
Everything has been running pretty smooth actually. There are currently about 800 users on the e-commerce with hardly any issues.
There's still things to do to reach feature parity, but splitting up the backend from the frontend has made everything much cleaner.
Multitenancy was the biggest thing that was implemented/tested and some things for power-users.
I also did some work to:
- make it easier to setup a shop the first time ( eg. When syncing 1000s of products)
- made a doc site for my clients and another one for their clients ( helps onboarding them).

B2B version has been deployed for over 6 months with minimal effort.
- No payment required from the customer
- Easy user management ( resetting the password is the most used feature)
- Linqpad scripts for syncing data ( which can be run through a cli-mode)
- Endpoints per integration ( current integration for syncing was one POS supplier in Belgium)
- New Backend! An SPA application with a BFF
- Customer can see attachments ( eg. invoices) on their account page
Since deployment, only new features had to be added which i'll discuss in a more lengthy post.
I had a good summer in between and used it as a break.

Comment
I finally implemented e-commerce filters in the shop, it was a bit more tedious than anticipated.
The Infrastructure wasn't a real problem with the Specification Pattern implemented and was fun too implement. Although an Expression Tree bug in the code took a while to fix (an and/or condition replaced all my expression variables and this caused errors in using deep expressions).
The translation from the URL to input was also pretty fluent. Everything is immediatly submitted in a HTML GET form, if a filter is changed.
The thing that took a while was the full implementation of the filters. Eg. Like some design decisions i made:
-
Eliminate the count on the amount of products that the filter would produce
-
Don't show the filter on mobile, but people on mobile can delete the applied filters
-
How to show the applied filters ( I first thought about showing it in the row on it's original place, then in the start. At the end, i decided to implement it on the original place + a seperate section with Applied filters)
-
The filters are currently not changed by the current url( eg. It will always show the full amount of products to have the most complete list as possible). Although it's trivial to implement it like this.
-
How to group the filter types, currently i have 4 types :
- Attribute filters
- Attribute Range Filters
- Price range filters
- Tag filters
All in all, it went rather well. But i had some delays because of backpain problems ( old ski accident) in combination with a full-time job.

Comment
After deploying the functionality of Customer zone, it was time to check the backups.
There is already a snapshot in place every day.
Additionally, all databases are backupped every night at 03:00 and send to a remote secure location at 04:00.
Data retention is max 61 days, since i use the rotating day paradigm where a backup is stored to the folder: backup\sql\servername@serverip\{currentDayOfMonth} and not all days have 31 days :)
The next "devops-day" will be to address encrypted backups. I already did some exploration and i'll need a certificate to do this. The problem is that i don't want the certificate to be accessible on a location accessible to the server.
So i will have to figure this out then.

Comment
Feature - Customer Zone [beta]
Explanation
Customers can now see their order history overview + detail
They can also update their password now.
It took a bit longer, because these were my first real DDD-modules. I spend a considerable amount of time ( 2-3 evenings + 1 day) to fine-tune the module architecture.
One of the nice things that was implemented, was using the Agregate ( DDD ) and updating the EF-entity ( = Updating my SQL Database). So only the properties that the Aggregate has were updated in the database.
The code for this is now ridiciously simple and 3 lines long:
public async Task Update(CustomerAggregate customer)
{
var entity = await m_db.Customers.FindAsync(customer.Id.Value);
var updated = m_mapper.Map<CustomerAggregate, Shop.Models.Customer>(customer, entity); //Update the entity with the customer Aggregate values
await m_db.SaveChangesAsync();
}
Bugfixes/enhancements [ Prod ]
- Improved sharing of pages through Facebook [enhancement]
- Registered accounts outside of the "checkout path" could have an issue with their orders [bugfix]
- Checkout out with a mobile sees their order summary now, which was hidden in the past [enhancement]
- Some minor layout changes

Comment
The sudden soft-launch of Belgian Brewed has been successfull and currently the errors have been reduced to 0, which i'm quite proud of since the shop had been launched early :).
Visitors
We already received orders across Europe ( Denmark, Germany, UK, Spain, Italy, Netherlands and Belgium). Even someone from Malta who had to use a proxy delivery service, since our package provider does not deliver there.
Erorr Logging
I'm using Elmah for error logging, it's been very easy to use and implement.
Security
I saw that i could see a users password when a error occured during the login process.
And since the elmah portal was publicly available ( by default) on the /Elmah endpoint. I immediately made it available for Admins only.
Then I noted all the errors that were happening, removed all the logs and then fixed them.
I also replace every form field that contains "password" and replace it with the value "HIDDEN" when a error occurs now. So it wouldn't be possible to log a password anymore.
Errors that occured
An unlucky visitor that started ordering in the beta phase, had an edge case when trying it again in the production site. It is fixed now.
Additional Filtering of errors
-
No logs are logged when a useragent contains the word BOT or DAUM, since they seem to have incorrect behaviour.
-
No log errors are logged when the path contains "wp-includes, .php, .js.map, .css.map". Which seem to be mostly crawlers that try to exploit 0-day vulnerabilities.
These 2 actions greatly reduced the amount of daily errors ( from > 50 daily errors, to 1 error logged yesterday)
So in general, i'm still monitoring if errors are occuring. But currently, everything looks ok (y)
Development
I'm currently working on the customer zone functionality. Which will contain the following functionality:
- GetMyOrders
- GetOrderById ( if the customer that ordered matches the logged in user)
- Update password
- View/Update address ( billing, shipping)
- Update avatar pic
Since i'm currently transitioning to DDD, this caused 2 more modules to be developped in the application:
- OrderingModule
- CustomerModule
Also, i've implemented the Specification Pattern which i'm quite proud of. It's the foundation of implementing filters on the shop so customer can filter for price and alcohol %.
The repository for orders currently looks like this:
public interface IOrderRepository
{
Task<IReadOnlyCollection<OrderAggregate>> GetOrders(ISpecification<OrderAggregate, IOrderSpecificationVisitor> spec);
Task<OrderAggregate> GetById(Guid Id);
}
A specification is anything that should be filtered ( eg. The specification to filter by a customer is the "OrderOfCustomer" Specification and it contains a Guid CustomerId)
In the infrastructure layer, i translate the specification to Expression Trees and can do additional logic on everything that is required. ( = Visitor Pattern )
Eg. While the Specificiation filters by the customer. I do not want to show the orders that are still in progress ( since i'm transitioning to DDD, the data is still in one SQL database ).
So in my infrastructure layer, i have to include the additional complexity for filtering orders and i have to exlude some OrderStates, namely:
- New = New orders that aren't orders yet
- OnHold = Eg. because payment has not occured yet
In practise. This means that in my core layer. The specification only contains a CustomerId to filter on .
And my infrastructure layer contains the additional filtering logic:
public void Visit(OrderOfCustomer spec)
=> Expr = expr => expr.CustomerId == spec.CustomerId && expr.Status != Enums.OrderStatus.Init && expr.Status != Enums.OrderStatus.OnHold;
Since i'm using expression trees and Entity Framework in my Infrastructure. These Expression trees are converted to SQL by the ORM.
:)

Comment
I'm happy to launch my first online shop on the platform: https://www.belgianbrewed.com .
We offer one of the most complete Belgian Beers inventory thanks to my partner.
My partner has > 900 Belgian Beers in stock all the time, which results in one of the most complete online beers shops that can be found.
The shop still needs some attention and improvements during the coming period :)
What I currently need to do:
Short term
- Inventory management ( stock was not required before, but since we have beers in limited editions, this became a sudden requirement)
- Suggest redirect to the user language ( eg. when a dutch person lands on the english language, suggest a redirect for the user)
- Improved detail layout ( not high priority, it's temporarily since the product descriptions are not always complete)
- backend check if a products "slug" is unique accross the shop
Middle term
- Integrate order history
- Implement filters on the shop
Long term
- Splitting up frontend and backend
- Full migration to DDD
There was a slight urgency all of the sudden. Since orders were coming in from the beta site a couple of days before the soft-launch ( we are still in the soft-launch period).
I recently migrated all the traffic from the beta http website, to the production one and the first orders were already coming in :)
The next project will be take-away and I already integrated some interesting approaches for delivery.
But i'll go deeper in the subject, when the first shops are coming live :)

1 Comment
1 Comment
-
1
Great store. Finally, I found a reliable and easy way to get great Belgian beers and have them delivered right to my doorstep. All my friends love beer, and when we get together for a picnic or fishing, we always give an order here in advance. Unlike me, my wife loves beer without alcohol, but in addition, there must be someone to drive the car. She usually orders the best non-alcoholic beer here https://free-beer.co.uk/. Honestly, I started to like it too. However, I drink non-alcoholic beer when I have to go to work or at home.
The b2c was practically complete for the client.
Currently prioritizing b2b layout for easing their administration for existing clients.
Since the launch got delayed multiple times, mostly from their end because they were taking over a company and their clients+products.

Comment
About
I have some small webshops based on WooCommerce. Since i hate the maintenance and have past experience in e-commerce sites i've decided to create my own.

Comment