Because web projects are always changing, you will find that different parts of your project need to be swapped out and replaced over time.
Many web frameworks boast of being "opinionated". Ufront is unopinionated - rather than providing the "one right way" to do things, we believe that what is right in one situation might be wrong for another, so we try to avoid locking you in to one particular way of doing things.
Perhaps in your app you need to send email to users.
You might choose to use the SMTPMailer
class - a sensible choice.
Later your needs change, and you need to be able to track open statistics, so you choose to use a service like Mandrill.
If you have hard coded to use SMTPMailer
, you will have to update your code in many places to use MandrillMailer
.
This could take a long time to change, and a long time to undo if you find out it was the wrong decision after all.
Or what if on your development machine, you don't want it to actually send email, you just want to test that the system works and not spam your users with messages.
We have a DBMailer
for this purpose, but you would have to replace every reference to SMTPMailer
with DBMailer
, and change it back before deploying to production.
This can be error prone.
Instead, we've created a UFMailer
interface.
This is a description of what a mailer looks like, and SMTPMailer
, MandrillMailer
, and DBMailer
can all use this definition.
Now instead of asking for a SMTPMailer
, or a DBMailer
, you can just ask for a UFMailer
, and it will use whichever one you have configured for the current environment.
This way of designing software is called Dependency Injection or Inversion Of Control.
Dependency injection means that the dependencies of your code, the things your code rely on, are injected automatically.
Rather than your class saying
public var mailer = new SMTPMailer();
you inject it:
@inject public var mailer:UFMailer;
Now your app can decide what sort of UFMailer to inject, and change it at a moments notice.
This is also called "Inversion of Control" because rather than the class deciding exactly which UFMailer it wants, that decision making power is done by the app, not the local class. You've moved the decision of exactly which implementation you need from the bottom of your code (the local class) to the top of your code (the application configuration).
In Ufront we use minject for dependency injection. See [[10.1.0 Dependency Injection]] for more details.
Here are examples of some of the replaceable parts in Ufront:
- [[02.1.0 HttpRequest]] - The details about the the incoming request. This has a different implementation on each platform/target.
- [[02.2.0 HttpResponse]] - The method for writing data back to the client. This also has one implementation for each platform/target.
- [[07.1.0 UFHttpSession]] - A way to save information for each session. Different implementations might save to the file system, a database, a cache system, or something else.
- [[08.1.0 UFAuthHandler]] - An authentication system - check if a user is logged in, and if they have permissions to do certain actions. Could get those permissions from the database, social media, LDAP etc.
EasyAuth
provides an implementation that uses database objects. - [[08.2.0 UFAuthAdapter]] - A simple tool to check if a username and password is correct.
EasyAuth
provides a handler for checking details against the database. - UFCache - A cache system that can cache variables between requests. Could potentially use Memcached, Redis, the file system etc.
- UFMailer - A tool to send email. Could use SMTP, or 3rd party API.
As you can imagine, different apps with different requirements will require different solutions to each of these problems: storing session data, sending email, authenticating logins, checking permissions etc. By providing simple interfaces that you can implement in many different ways, Ufront lets you start with what works now, and change the implementation later if your requirements change.