Showing posts with label architecture. Show all posts
Showing posts with label architecture. Show all posts

Thursday, February 7, 2019

Lekcja architektury prosto z Castoramy

Odkryłem w Castoramie fakturomaty. Wow!

Zawodowa ciekawość zmusiła mnie do skorzystania, a zawodowy talent unieruchomił fakturomat już w trakcie pierwszego użycia.

Najpierw pojawił się komunikat "Archiwizowanie paragonu", a następnie "Błąd wystawiania faktury" czy coś w podobie.

Poprosiłem o pomoc, Pani otworzyła dolne drzwi fakturomatu i oto widok jak na zdjęciu:


Okazało się, że fizyczne ArchiwumParagonów, które chyba niechcący wyłożyłem, to kartonowe pudło do którego wpadają paragony po przeskanowaniu przez czytnik. Sama esencja KISS jak zgaduję.

Pani pogrzebała w archiwum, feralny paragon się znalazł (heurystyka "mniej więcej po kwocie") i po ponownym przetworzeniu fakturomat wypluł fakturę.

Wniosek praktyczny: Jeśli user zgłasza, że Twój soft ma kłopoty z działaniem, upewnij się, że pod pięknym interfejsem, nie śmigają aby kartonowe pudła :P (może to OK, a może nie)

Ogromny szacunek dla personelu Castoramy, który skutecznie uniemożliwiał mi zrobienie porządnej dokumentacji fotograficznej z miejsca zdarzenia :D

Monday, September 26, 2016

Rethinking Software Architecture

From time to time everybody feels that the architecture of the software one develop is quite closer to a big bull of mud that the screaming architecture.

I give you plan of a workshop you may lead with your team. This is a part of my ArchitecturalKata for an Agile Team workshop.

What is the Goal

We want to achieve agreed and clear vision of the architecture. I am really pragmatic person. So I am not going to push you to introduce some sexy solutions as: DDD, Clean Architecture, CQRS, Event Sourcing and stuff. It may happen it may not.

There are many criteria to meet to achieve these architectures. Here, our goal is to move your architecture into a better place, even it mean a step forward. We want the architecture to be more testable, maintainable and more decoupled. That's all.

What We Need?

  • a team who work on the system (recommend no more than 10 people)
  • 1-2 days
  • silent classroom
  • space on the walls or on the floor
  • quite big table or space on the floor
  • couple blocks of sticky notes in many colors
  • colored markers
  • pair of scissor
  • printer paper
  • big flip-chart sheets
  • paper tape
  • a computer with a code baseline
  • beamer (useful but not necessary)

See Responsibilities

Because flow of that workshop varies in some details depends on the software size and granulation, I give you an example for the quite big legacy system (developed from more than a decade, where primary building blocks are components containing many classes).
  1. First, capture all your components. Write each on a sticky note. It may be hundred of them or more but not so much :)
  2. Then look into the source code and try to name responsibilities. Responsibility is a role of the component and the way how it serve to others. So, you will find them looking at the public methods, events published, signals sent. Sometimes one strange method is one responsibility, other time group of related method define a responsibility.
  3. That's important: name only those responsiblities you see in the code, not those you think they are
  4. Don't be too detailed with the granulation of the responsibilities. Be ready to regroup and rename them any time you want
  5. Probably you know that in a legacy code components have many responsibilities, so name them explicitly and write them on small stickies. Next put small stickies on components they come from
  6. You will see than you have two to four components which looks like stars with its satellites :) These are components where single responsibility principle was really abused. There is much code inside

Redefine Responsibilities

Now its time to redefine responsibilities of the components. Organize your components obeying following rules:
  • A component has exactly one coherent responsibility
  • You are allowed to add new components
  • You are allowed to remove components
  • You are allowed to rename components
So now:
  1. Add/Remove/Rename components and reorganize responsibilities stickies
  2. Repeat until every single component have sort of small stickies and all of them describe one coherent responsibility

Redefine Communication Between Components

  1. Now draw directed lines representing communication between components and note purposes of a communication just right above the lines. A line starts from a component which initiate communication.
  2. There is one rule during this exercise: a line cannot cross an other line. I will explain why at moment.
  1. Sometimes you will discover a cyclic dependency between components, so remove it, if needed.

Cluster Components

  1. The rule "don't cross the line" caused that components which work closely are stuck closely.
  2. Try to circle clusters of closely related components. But there is important rule: look for the clusters related to the functionality of the system. You may ask yourself: What is the core cluster where we have minimal deliverable functionality? How to cluster components to extract potentially optional functionalities?
  3. Name these clusters

Define Clusters API

Now we want to see high-level view. We want to extract fine grained and decoupled clusters enclosed in modules, processes or separate applications.
  1. First define an API for the clusters.Finally we want cluster-to-cluster communication instead of component-to-component. A component-to-component communication is fine inside the cluster, but not between them.
  2. API receives communication from other clusters and distributes it inside of the cluster. It also translates the inside-cluster communication into outside one.Technically that translation layer will be some combination of facades and adapters.
  3. Defining API remember that we have: exposed interfaces and required interfaces. Define both.
  4. Also explain how the inside-cluster components talk to each other.
  5. This is important: obey the encapsulation rules: nothing from inside of the cluster is seen to the outside world

Define Communication Between Clusters

  1. Draw communication between clusters. It will be API-to-API communication, it mean exposed interface to required interface.
This is it. Artifacts and discussions during that workshop bring quite clear understanding of the current state and well defined vision were want to be if about architecture of our software.

How long it takes to refactor the code?

Wel, it depends ;). I know the cases (software of that size) where it took a year or so total. Remember that visioning is a one thing, but bringing the vision into reality is completely different story.

What Next?

One of the next steps is a really tricky stuff. We need to prepare plan how to communicate our technical objectives to managers and all those people who have an authority to say YES or NO to our refactoring work. On the end we want them to support our new architecture.

Working strategies to convincing sponsors are the key part of my ArchitecturalKata for an Agile Team workshop. If you want me to lead this workshop for your team, please contact me at the contact form.

Wednesday, March 5, 2014

Presentation Layer Under Control

Hower there is bunch of patterns addressing presentation layer issues, implementing clean presentation layer in Java desktop application is tricky task. Main problem for me is a lot of mess inside. I'd like to present my approach to take control of presentation layer, which is combination of existing ideas such: Supervising Controller, Passive View, Presentation Model, Publish-Subscribe, Observer.

This will be an approach, not a pattern. The reason is a pattern is something widely used and this will be just set of tips. So you won't be able to apply it by just repetition some steps, but by interpretation the approach in the current context and SOLID analysis. But if you are not familiar with patterns listed above and presentation layer issues of desktop application, try Mediator pattern or SmartUI approach first. You will have some mess in the code but still under control.

So, if you want to take a look on risky implementation of a presentation layer, check out this example. As far I remember some folks call this SmartUI anti-pattern.

Well, the UI is really smart in the example. It contains child UI components, implements listeners and does both UI and business processing. This GUI is simple but continuing this approach leads to code which is hard to understand, hard to test, hard to develop.

The idea

Let's list challenges for the desktop applications with visual presentation layer:
  • communication between controls
  • managing application state - there are three types of it: screen state, session state and record state
  • type conversion between view and rest of the application
  • handling user events
  • keeping GUI responsive - typically with threads
  • testability - Humble Dialog aka MVP is a solution

Take look on the figure.

In the typical application all controls are grouped by their business purpose and UX rules. Let's treat the controls arrangement as being not so important for the application architect. What is important that we have many groups of controls, each group has its business purpose and groups need to communicate with others. Let's call these groups components.

However components might be visually arranged in a hierarchy (depends on UX designer), from the source code side they are totally equivalent. Component to communicate with other component use an event bus. When an event comes to a component it updates its state (what mostly means a user view is changed) according to the subscribed event.

Using event bus gives all benefits of the Publish-Subscribe pattern. You may use topics or even two or more event buses for really complex GUIs.

This was an idea. Now let's take a look how a component is designed inside.

Structure of a component

Five parts works together as a component.
partresponsibilityout of responsibility
Form * is coupled with a particular GUI technology
* builds itself
* has private helper methods to working with embedded controls
* calculates a control arrangement
* business processing
* calling calling services
* validation
event publishing
FormEventHandler * implements all technology-specific event listeners
* might be decomposed into specialized handlers e.g. DragAndDropHandler
* subscribing events other than technology-specific
* business processing
* calling calling services
* validation
* event publishing
View * exposes methods for getting and Form state (see Nice Move with ValueSetter)
* typically is implemented by Form classes
* event publishing
* event subscribing
ViewObserver * notifies that something happened on a Form
* typically is implemented by the Presenter class
* event subscribing
Presenter * calls business services
* publishes events addressed other components
* subscribes on events from other components
* business processing (except of very rarely cases)

So how you see, a component talks to other components via event bus, but asynchronous communication inside the component is implemented via Observer Patterns. There is nothing wrong with using event bus inside the component also. But in my opinion the solution with event bus outside- and Observer inside- is more clear and easier to test.

and how it works?

Here you have a diagram how a user request is processed by a component.

and also how components talk to others:

ViewModel - what is for?

The problem is how to deal with screen state (keeping Form as stupid as possible), especially when you work with the remote backend and you need to synchronize the client with the server.

Possible solution to this is introducing a session state which is in-memory copy of screen state. Presentation Model pattern do that. The pattern represent the state and behavior of the presentation independently of the GUI controls used in the user interface. It is possible to find Presentation Model structure in this approach. These are: View interface and Presenter.viewModel property.

However implementing Presentation Model is not the goal, I have borrowed viewModel part from the pattern. Why?

In regular MVP pattern View exposes methods to set and get state of Form controls. It's something like that:
public class DetailsPresenter implements DetailsViewObserver {

  private DetailsView view;

  public void handleRequest() {
    String name = view.getName();
    String name = view.getTitle();
    Date dayOfBirth = toDate( view.getDayOfBirth());

    //call services

    view.setTitle( newTitle );
    
    //...
  }
}
This approach causes lots of set/get methods in the View interface. Because the fact that some controls states are needed to business processing the Presenter handler methods are full set/get/add invocations.

An alternative to this is full-featured Data Binding. When something changes on the screen it is automatically reflected to the viewModel and vice versa. Really magic Data Binding means lots of listeners, controllers and events going there and back again.

So, what instead? Map? Well, map might be a bag for everything, but idea is good. I use the apache-commons DynaBean as viewModel implementation. ViewObserver exposes the method to notify about changes of screen state.
public class DetailsPresenter implements DetailsViewObserver {

  private DynaBean viewModel;
 
  private DetailsView view;
 
  public void buildViewModel() {
    viewModel = new LazyDynaBean();
    viewModel.set("name", "Jack");
    viewModel.set("title", "Dr.");
    viewModel.set( "dayOfBirth", new Date());
  }

  public void handleRequest() {
    String name = viewModel.get("name");
    String name = viewModel.get("title);
    Date dayOfBirth = viewModel.get("dayOfBirth");

    //call services

    view.updateControl( "title", viewModel.get( newTitle) );
    //or updateAllControls()
  }

  @Override
  public void propertyChanged(String property, Object value) {
    viewModel.set(property, value);
  }
}

public class DetailsFormListener implements DocumentListener {
  
  private DetailsViewObserver viewObserver;

  public void changedUpdate(DocumentEvent e) {
    viewObserver.propertyChanged("title", e.getDocument().getText());
  }

  //...
}

Implementation details

You may fetch examples from my Github profile.

pl.bnsit.architecture package

This is basic template of the approach.

pl.bnsit.flights package

This is an example with coule windows, validation and so on. But I followed the typical llayered architecture.

pl.bnsit.onepieceflow package

There I went a step ahead. I tried to write simple Kanban board. But I started thinking iin a component-like way. I asked myself What are independent components on this GUI? and answered: a card, lane containing set of cards and board containing set of lanes. Then I realized that the independency might be considered through all application layers, not only inside the presentation layer.

Continuing this way of thinking we come the architecture with small, well-defined components anchored in the business domain, which are working together to do a business task.

Type conversion

Notice that some type conversion must be done between viewModel and Form. I did this with TypeConverter interface:
public interface TypeConverter {
  String toString( Object anObject );
  Object fromString( String str);
  Class type();
}
Conversion is done when the control state is updated:
public class AddingCustomerForm extends JFrame implements AddingCustomerView {
 
  private Map converters;

  public void updateComponent(String id, Object value) {
    Object convertedValue = convert(value);
    JComponent component = components.get(id);
    ValueSetter.get(component.getClass()).set(component, converterValue);
  
  }

  private Object convert(Object value) {
    Object converterValue = value;
    
    if( converters.containsKey(value.getClass()) ) {
      TypeConverter converter = converters.get(value.getClass());          
      converterValue = converter.toString(value);
    }

    return converterValue;
  }

and back, when Listener notifies via ViewObserver that control state was changed.

Wednesday, February 5, 2014

Extremely Pragmatic Software Architecture

Since the Measuring Software Architecture I have beeing digging through two systems written in not so mainstream technologies and some crazy idea occurred to me.

Do you know what is the biggest problem for a developer when one starts to develop a system? Lots of Mess (upper case on purpose). All that hundreds of hundreds interfaces with one, two or three methods. All that Abstract*, *Impl, Generic*, *Helper, *Support classes, which implement all mentioned interfaces make a developer really confused.

Are the patterns or DDD or something else the remedy for that? I am not sure.

First. You may think that applying quasi-remedies above reduces cost of introducing a newbie to the project. I disagree. A developer learns the technical meaning of the source code very quickly. Really big cost is acquiring in business domain.

However domain-agnostic developer writes poor code, but perfectly understanding how the domain works is not critical for one to understand what is technically going on in the code.

To be honest, I have met few software professionals who perfectly understand how the domain worked. Most of us knows just only enough (or little bit less) to make the feature working (at the moment).

Second. You may think that applying quasi-remedies increases code-readability what improves communication, what improves quality, what improves velocity, what improves business value. Well, this is true, but..

I observed when a project breaks all code-readability techniques and crazy naming conventions and macro-design techniques are used CONSEQUENTLY by a team, then some phenomenon happens. People align their perception to consequently used (even crazy) conventions and techniques. And then they are becoming productive developers. It takes time, but this is the fact.

So, my idea is the crucial thing for working effectively with software architecture is...keeping order inside the project, packages, folders, classes, methods and so forth. Order means: not so much code in one place, not so much dependencies between items and avoid cyclic dependencies if possible.

Do you know what I mean? No methodologies, no *Driven* approaches, no patterns maybe, just ordinary old-fashioned order.

When you focus yourself on listed approaches first, you may forget what they were invented for. Being focused on keeping order doesn't make you most modern developer, but you will know what will be going on in the project, what to do and how to keep the code simple and clear at the moment. And I think this knowledge is enough. At the moment, of course.

Sunday, December 22, 2013

Measuring Software Architecture

I found interesting approach on the subject being developed by Structure 101 team. They introduced Excessive Structural Complexity, XS in short.

They pointed that an architecture may be seen in two dimensions: Tangle which is structural complexity on design level and Fat which is "too much code in one place". More precisely:
Tangle
it's set of items being in a cyclic dependency; tangle is counted on packages level; for each set of tangled items a Minimum Feedback Set (MFS) is calculated which is a minimum set of dependencies that would need to be removed in order make the graph clean
Fat
for methods it's Cyclomatic Complexity, but for clases and packages it's a number of dependencies between them
XS is an excessive ie. unnecessary complexity, so some thresholds must be given. Indeed, you are able to define thresholds for Tangle, and also for Fat on each method, class, leaf package, package (design) levels. You may also trust the defaults. Those come from Structure 101 team experience.

After normalization, XS gives one single number expressing an excessive complexity of a piece of code. Depends on code level, a XS might be Fat mean, Tangle mean or sum of these two.

It is very interesting because it gives fancy measure for each architectural level. It works well on: methods, classes, packages, layers and more abstract components levels.

Intersecting XS and number of commits to a repo, we might more accurately say what is worth to be refactored and what is not. (Idea of this intersection comes from Michael Feathers, but, as far I remember he use the Cyclomatic Complexity instead of XS)

For more information read the whitepaper or try out the tool.

Thursday, December 19, 2013

Frame-of-our-work

Working with a software and repeatable tasks, teams walk form a hard-coded solution to a generic framework and back with confusion. Let's search for the third way.

Table below presents some attributes of those two types of approaches:

  adding a new code maintaining a design redesigning as time goes by...
comment How much time is needed to add new functionality which respects a framework architectural boundaries? Any piece of code must be maintained from now to the end Sometimes new functionality is out of a framework architectural boundaries. So that one need to be redesigned and refactored What will be happening with the code in the future?
hard-coded solution offhand n/a there's no a design n/a there's no a design maintaining becomes really hard
generic framework easy if one knows how a framework works needs some effort might be hard and expensive two scenarios might happen: a framework becomes more and more generic till someone decides to kill the project or a framework becomes more and more coupled with business-specific code and then it goes toward a hard-coded solution

Frame-of-our-work

If both of extremes are wrong what is the third possible way? This is: don't build THE FRAMEWORK. Build THE FRAME-OF-OUR-WORK instead. Please, pronounce these two words: framework and frame-of-our-work. I am serious. So now:
  • What a responsibility do you you put on framework and frame-of-our-work?
  • What is possible to do with framework and frame-of-our-work?
  • What is impossible to do with framework and frame-of-our-work?
  • What would you give up changing a focus from framework to the frame-of-our-work or opposite?

You know, naming is important. When you label a project you give a team the direction of work and it determines what kind of work will be done or undone. Focusing a team on building a framework you are at lots of cognitive biases. A framework word is so imprecise that you may do more work than you intended to. Besides we do really like to build frameworks - check Source Forge or Google Code :).

Frame-of-our-work focus you on what are we really doing now? You need to inspect tasks a team do, before you start building a frame-of-our-work. This is nothing but Single Responsibility Principle.


I put a characteristic a frame-of-our-work into the table:

  adding a new code maintaining a design redesigning as time goes by...
comment How much time is needed to add new functionality which respects a framework architectural boundaries? Any piece of code must be maintained from now to the end Sometimes new functionality is out of a framework architectural boundaries. So that one need to be redesigned and refactored What will be happening with the code in the future?
hard-coded solution offhand n/a there's no a design n/a there's no a design maintaining becomes really hard
frame-of-our-work fast easy needs some work but it's acceptable it's sensitive on rapid changes of business processes because it's highly coupled with them
generic framework easy if one knows how a framework works needs some effort might be hard and expensive two scenarios might happen: a framework becomes more and more generic till someone decides to kill the project or a framework becomes more and more coupled with business-specific code and then it goes toward a hard-coded solution

Although a frame-of-our-work is not so sexy as a framework does, my observation is the cost of redesigning a frame-of-our-work is mostly lower than the cost of maintaining a generic design of a framework.

A simple example

You build desktop application and you search for a good way to implement the GUI logic. It's not trivial in Java:) Focusing on framework you might decide to implement a generic Presentation Model with fancy widgets binding. This is very flexible solution, but...what kind of flexibility do you really need in your project?

On the other side, building a frame-of-our-work you might decide to prepare very simple Presentation Model where you have one stupid POJO per one user screen.

Both are well known approaches, but the difference is how much work will be done.

At last...

What if you really discovered a generic domain and a generic framework would be very useful? What then? Fork the project, find a sponsor in your organization and build the framework. But never ever build a generic frameworks as a part of your regular workflow.

Monday, December 16, 2013

What Exactly Are Patterns?

I found very interesting paper: Developing GUI Applications: Architectural patterns Revisited by Alexandros Karagkasidis

Implementing presentation layer is a really tricky. It is at least so complex as implementing a domain layer and not at all less important. There are lots of approaches to the subject, from the most comprehensive MVC, MVP, PAC and HMVC to the most detailed being described by Martin Fowler or vendor-specific approaches i.e. MVVM (which might be described in a Flowler's language as well).

According to Alexandros all these share same problem: work well for simple cases. My own understanding is the core is how a GUI functionality should be exactly mapped to structural elements of a pattern.


Let's take MVC pattern. One may use this pattern on widget, container or window/frame level. Decision is left to a developer. Besides, from my experience an approach is highly coupled with a functionality flow and refactoring is really hard. Let's say you decided to implement MVC on container level and then a CR requires some small interaction between a frame caption, button, and tab color. You may hardcode it or extract a smaller MVC triad. With this, a refactoring is quite time-consuming.

My point is: non-GoF patterns define a general idea but it is not enough to implement them in a complex case. My observation is teams sometimes needs lots of iterations to find their own way to implement an architectural pattern. It may takes months or even years.

Mentioned article doesn't contain any solution, but it is worth to be read to give you an insight into a complexity of GUI development.

Monday, December 9, 2013

There Is No The One Way in Software Architecture

At last they publish the presentation I did at Confitura Conference. It's in Polish, sorry.

This presentation contains my thoughts on software architecture. I'm talking about:
  • importance of a technical context
  • importance of a business context
  • core domains which may belongs to the both technical and business areas
  • few points being my own-made steps to verify vision of new architecture
I wish they published my 33rd Degree presentation, where I was talking about looking the Holly Grail and all that *Driven madness.

Tuesday, April 30, 2013

UniversalProblemSolver



Jeżeli Twój kod przypomina coś takiego w/w, to pamiętaj, że wkrótce skończy Ci się paliwo i możesz już na zawsze pozostać na orbicie okołoprojektowej ;)

Na początek możesz przeczytać artykuł Zawsze pracuj na twardych danych.

Tuesday, April 16, 2013

Koder kodzi, architekt architekci

Na 4Developers odbył się Panel dyskusyjny nt. architektury, architektów i architekcenia:). Trudno powiedzieć, żebyśmy udzielili odpowiedzi na jakieś konkretne pytania, ale było miło. Chociaż padało pytanie o rolę/stanowisko architekta i jego miejsce w procesie, to wydaje mi się, że byłem odosobniony w zdaniu, że bez tradycyjnego architekta można się obejść. Podłapałem tę myśl od Scotta Amblera i po moich doświadczeniach z różnymi zespołami jestem przekonany, że to ma sens.

Ankietowałem kiedyś obsesyjnie, architektów (różne stanowiska różnie się się w firmach nazywają i mimo istniejących jakichś tam typologii, każdy nazywa je sobie po swojemu), aby namierzyć, co konkretnie robi osoba określana jako "architekt". Zbiorcze zestawienie przedstawiam poniżej.

Jaki jest Twój zakres obowiązków jako Architekta?

  • Odpowiadanie na pytania dotyczące struktury systemu
  • Definiowanie interfejsów do/z systemów powiązanych
  • Decydowanie o wykorzystywanej technologii na poziomie systemu
  • Projektowanie deploymentu systemu
  • Decydowanie o organizacji i rozmieszczeniu komponentów? Który kod umieszczany jest w bibliotekach, programach, gdzie są zewnętrzne biblioteki, co jest ładowane statycznie co dynamicznie?
  • Utrzymywanie dokumentacji dotyczącej architektury system
  • Zdefiniowanie struktury dokumentacji
  • Określenie, które domeny biorą udział w danej funkcjonalności (co zapisują, co trzeba odczytać), jakie nowe interfejsy trzeba udostępnić, a jakie zmienić
  • Prowadzenie prac R&D
Z jakimi pytaniami należy zwracać się do Architekta?

  • Ja ma odbywać się interakcja pomiędzy modułami/częściami systemu?
  • Jakiej technologii najlepiej jest użyć do dostępu do bazy danych?
  • Jak zabezpieczać pliki konfiguracyjne w systemie/aplikacji?
  • Jaki model konfiguracji serwerów (np. Tomcat) przyjąć dla produktów/aplikacji webowych?
  • Jak należy definiować zewnętrzne interfejsy systemu?
  • Jakie elementy systemu i według jakich kryteriów mają podlegać walidacji?
  • Jakie są główne komponenty systemu?
  • Czy wymiana danej technologii mogłaby wspomóc
    działanie systemu?
  • Jakich zmian wymagać będzie wprowadzenie danej
    funkcjonalności?
  • Jak wygląda flow w danym komponencie (np. przetworzenie wiadomości w kolejce)?
  • Jakie są wymagania funkcjonalne dla danego produktu
  • Jak konkretny proces biznesowy realizowany jest w architekturze?
  • Jak wygląda zużycie zasobów sprzętowych i programowych w danym okresie czasu?
  • Jakie narzędzia i skrypty należy wdrożyć, aby usprawnić proces wytwarzania oprogramowania?
  • Jak zorganizowana jest infrastruktura sprzętowa (jakie hosty? jak są powiązane?) i programowa (jakie wersje? gdzie?) systemu?
Z jakimi pytaniami NIE należy zwracać się do architekta?

  • Czy ta funkcjonalność działa poprawnie?
  • Jakie projekty idą na najbliższy realese?
  • Jaki będzie koszt projektu X?
  • Jak działa ta funkcjonalność?
  • Jak z korzystać z tego pakietu/biblioteki?
  • Do jakich zadań planowany jest Pan X?
  • Kiedy wypada deadline testów systemowych
  • Jaki algorytm wykorzystać do…?
  • Czy następujące zachowanie systemu jest poprawne?

Jeśli popatrzeć na wymienione obowiązki, to trudno oprzeć się wrażeniu, że z niemal wszystkimi z nich może sobie poradzić zespół jako taki i nie potrzeba do tego wydzielonego architekta. Jedyna różnica między w/w architektem a "zwykłym" programistą jest taka, że ma on architekt większe doświadczenie (ale nie to mierzone w latach) i większą wiedzę o systemie.

Wydaje się, że potrzeba powołania osoby w randze architekta wynika z potrzeby rozwiązania następujących problemów:

Można odnosić wrażenie, że tego typu architekt został wymyślony po to, aby wyousourcować w/w problemy. Sęk w tym, że staje się on wąskim gardłem procesu. Po prostu nie nadąża z pracą. Wtedy trzeba powołać kolejnego i kolejnego i kolejnego. Po pewnym czasie okazuje się, że architekci zajmują się głównie sprawami organizacyjno-konfiguracyjno-spotkaniowymi, a programiści przestają czuć się odpowiedzialni za architekturę, gdyż ta "należy" do architektów. I wtedy koder zaczyna kodzić, architekt zaczyna architekcić, a architektura zaczyna dryfować.

Monday, April 8, 2013

Dogmat architektoniczny, czyli nie wkurzaj programistów


W pewnej organizacji, człowiek o na stanowisku Architekt Korporacyjny zrobił zebranie, na którym objaśnił nową wizję architektury, nazywając ją dogmatem architektonicznym.
Programiści odwdzięczyli mu się zamieszczonym zdjęciem oraz drobiazgową analizą pojęcia dogmat architektoniczny, którą za pozwoleniem cytuję w całości.

Dogmat (z greckiego dogma - mniemanie, osąd, postanowienie, nauka),
  1. W starożytności postanowienie prawne zgromadzenia ludu lub senatu, także twierdzenie filozoficzne uznawane przez przedstawicieli danej szkoły za nie podlegające dyskusji.
  2. W teologii katolickiej zdanie, które Kościół na mocy decyzji swego Urzędu Nauczycielskiego lub papieskiej albo soborowej definicji uznaje jednoznacznie za objawione przez Boga, dając równocześnie do zrozumienia, że zaprzeczenie mu oznacza popadnięcie w herezję. Dogmat nie podlega odwołaniu, choć w związku z tym, że ponadczasowe objawienie łączy z elementami uwarunkowanymi historycznie, może zostać poddany tzw. aktualizującej interpretacji lub "zapomniany" (przestaje być przywoływany).
  3. W teologii prawosławnej dogmat rozumie się podobnie jak w katolicyzmie, jednak prawo jego stanowienia przysługuje wyłącznie soborom. Ponadto Kościół prawosławny nie ogłasza dogmatów, które nie są bezpośrednią interpretacją faktów przedstawionych w Biblii (dlatego np. odrzuca katolicki dogmaty o niepokalanym poczęciu NMP).
  4. W religioznawstwie określenie końcowego etapu ewolucji doktryny religijnej - ta jej wersja, która uznana została przez hierarchię danej religii za oficjalną i ostateczną.
  5. Potocznie osąd lub pogląd akceptowany bezkrytycznie, wyłącznie z racji zawierzenie autorytetowi.

Architektura - etymologia terminu
Słowo „architektura” pochodzi od łacińskiego „ architectura” oraz od greckiego „αρχιτεκτονική” (architectu) i oznacza budowniczy, z kombinacji „αρχι-” (archi-), szef i τέκτων (tekton) budowniczy, stolarz. Podczas gdy główne znaczenie słowa „architektura” dotyczy środowiska budowy, przez rozszerzenie terminu oznacza sztukę i dyscyplinę tworzenia aktualnego planu każdego kompleksu lub systemu.

Dzieło architektury
Obiekt, dzieło architektury, powinien odpowiadać zamierzonej funkcji, celowości technicznej, wymaganiom ekonomicznym i estetycznym, a przede wszystkim, dążeniom i oczekiwaniom użytkowników jego przestrzeni lub przeznaczenia.
Realizacja zadań lub obiektów architektonicznych wymaga korzystania z osiągnięć innych dyscyplin, m.in. sztuki, filozofii, inżynierii, techniki, budownictwa, statyki, socjologii, psychologii, urbanistyki, nauk ekonomicznych. W ciągu wieków zmieniały się zarówno zakres obowiązków i koniecznej wiedzy architekta, jak i samo pojmowanie architektury.

Różne definicje architektury
  • Potoczne rozumienie związane jest z pochodzeniem słowa architektura: architectura (łac.), zapożyczonego przez Rzymian z greckiego architekton – budowniczy.
  • Według Witruwiusza (O architekturze ksiąg dziesięć) architektura polega na zachowaniu trzech zasad: trwałości (Firmitas), użyteczności (Utilitas) i piękna (Venustas)
  • Według Egona Eiermanna (Grosse Architekten): Architektura nie ma nic wspólnego ze sztuką, stanowi czysty proces rozumowania. Architektura powstaje dziś wedle uwarunkowań ekonomicznych, konstrukcyjnych i funkcjonalnych
  • Niektórzy ze współczesnych neomodernistów (jak np. Vacchini, Snozzi, Galfetti) definiują architekturę, jako rzecz bezużyteczną, która pojawia się dopiero wtedy, gdy potrafimy przekroczyć granice banalnej użyteczności

A zatem: Dogmat architektoniczny
Potocznie pogląd lub osąd akceptowany bezkrytycznie, wyłącznie z racji zawierzenia autorytetowi, definiujący architekturę jako rzecz bezużyteczną, która pojawia się dopiero wtedy, gdy potrafimy przekroczyć granice banalnej użyteczności.

Tuesday, February 19, 2013

Strategiczna refaktoryzacja

Dlaczego "strategiczna"? Ponieważ od kiedy po raz pierwszy zobowiązałem się do "Niech Pan coś zrobi z tym kodem", stało się całkowicie dla mnie jasne, że techniki refaktoryzacji to za mało. Refaktoryzacje refactoring.com, czy Refactoring to Patterns są genialne i precyzyjnie opisują jak transformować kod z jednej postaci w inną, lecz są to zbyt niskopoziomowe przekształcenia o zbyt małej "sile rażenia", aby mogły przynieść wymierny efekt w mocno zagmatwanym i wciąż zmieniającym się kodzie.

O czym dokładnie mówię? Mówię o projektach rozwijanych od dziesięciu lub więcej lat. Ilość wierszy kodu liczona jest wtedy w milionach. Wyobraź sobie ten projekt, a w nim plik *java (zawierajacy jedną klasę) o rozmiarze 2.8MB, co przekłada się na około 60k wierszy kodu w klasie. Dalej wyobraź sobie, że podobnych klas jest kilkaset w tym projekcie. W takim razie jaką konkretnie zmianę spowoduje chociażby Replace Conditional with Polymorphism? Albo inaczej: ile pieniędzy w kontekście najbliższego kwartału pozwoli to zaoszczędzić? Wydaje mi się, że takiego bilonu w Polsce nawet się nie bije. Wprowadzona zmiana utonie w odmętach wszechogarniającego spaghetti. A system wciąż się rozwija, kodu wciąż przybywa.

Kluczowe założenie: To ma sens

Szczegóły na ten temat umieściłem w poście Megasoftwarecraftsmanshipper. Moim zdaniem nie da się sensownie przeprowadzić znaczącej refaktoryzacji, jeśli w pierwszej kolejności nie zrozumiemy dokładnie przyczyn, dla których kod znajduje się obecnie w takiej a nie innej postaci. Bez takiej analizy najczęściej podejmowane są refaktoryzacje, które od dawana chodzą zespołowi po głowie, są ciekawe od strony technicznej, bywają czasochłonne i jednocześnie nie prowadzą do znaczącej poprawy.

Może jest to banalne, ale tego typu analizę najłatwiej przeprowadzić przy założeniu, że architektura kodu ma sens, że komuś kto ją tworzył, przyświecały racjonalne przesłanki do podjęcia decyzji, które podjął. Zamiast zadawać sobie pytania: co to $^&#@#$ jest!? , itp., stawiasz następujące: "co musiałbym myśleć, aby stworzyć taką architekturę? w jakiej sytuacji musiałbym się znajdować?". Ponoć indiańskie przysłowie mówi, że "możesz kogoś oceniać dopiero wtedy, gdy przejdziesz milę w jego mokasynach". Coś w tym jest.

Zidentyfikuj obszary refaktoryzacji

Nie wszystkie fragmenty systemu są jednakowo ważne. Refaktoryzuj te, które są najważniejsze. Jako atrybuty do klasyfikacji możesz wziąć pod uwagę:
  • najczęściej zmieniające się fragmenty
  • fragmenty, na które raportowanych jest najwięcej błędów
  • fragmenty, z którymi programiści mają najwięcej problemów
  • fragmenty najmniej/najbardziej pokryte testami

Pomocnym narzędziem do analizy w/w atrybutów są raporty z Twojego traca albo repozytorium. Kilka informacji znajdziesz w artykule Your VCS – the forgotten feedback loop

Najpierw zatrzymaj degradację kodu

Idę tę opisałem na na przykładzie radzenia sobie z workarounds w kodzie.
Innym, bardziej powszechnego użytku przykładem, może być skorzystanie z zasady Interface Paritioning.



Gdy mamy wielgachne (60k w pliku txt; rozmiar metod od 20 wierszy od 1k) klasy jak na rysunku, od których zależy wiele innych, to pierwszym problemem nie jest ich rozmiar, lecz to, że sytuacja wciąż się pogarsza ze względu na trwające prace, a więc stale przyrasta ilość klas zależnych od naszego potwora. Jeśli zabierzesz się za rozkładanie tej klasy na mniejsze kawałki, to może to zająć tyle czasu, że w końcu spotkasz się z Jeźdźcami Apokalipsy. Jeśli jednak na najpierw posegregujemy metody z potwora pomiędzy interfejsy, to spowoduje to zatrzymanie postępującego pogarszania się kodu.



Skupiamy się, na zatrzymaniu powstawania nowych zależności. Empirycznie sprawdziłem, że poszatkowanie na interfejsy klasy zawierającej ok 1k metod, dwóm osobom, które w miarę często z niej korzystają zajmuje jakieś 4-6 godzin. Po takim zabiegu można sobie już zaplanować spokojne wydzielanie mniejszych komponentów z potwora.

Drugim przykładem na powstrzymanie postępującego bałaganu w kodzie jest Replace Method with Method Object. Ten refaktoring kojarzy mi się z pączkowaniem drożdży.



Chodzi tu przede wszystkim o to, aby "wypchnąć" dużą ilość kodu poza wielgachną klasę i tam sobie już spokojnie rozkminiać dany fragment i go porządkować.

Te dwa sposoby działania bardzo dobrze ze sobą współpracują:
  1. Interface Partitioning - tworzy w okół potwora skorupę, dzięki której nie może on już wpływać na inne klasy
  2. Replace Method with Method Object - pomaga rozczłonkować potwora na kwałki



Zarządzaj refaktoryzacją również na poziomie mikro

Oczywiście umiejętności programistów są jednym z kluczowych blokad przeciw pogarszaniu się jakości kodu.



Mam tu na myśli powtarzalny proces pracy z kodem, który nazywam Naturalnym Porządkiem Refaktoryzacji. Szczegółowo możesz się zapoznać z tą koncepcją tutaj:

Hipoteza o pewnym Punkcie:)

Przez analogię do Promienia Schwarzschilda mam taką hipotezę, że dla każdego systemu istnieje taki poziom bałaganu w kodzie, po przekroczeniu którego, zespół nie jest już w stanie kompetencyjnie podołać refaktoryzowaniu systemu i każde podjęte działanie pogarsza tylko jakość kodu.

Żeby znaleźć rozwiązanie w tej sytuacji, należy zacząć inaczej myśleć o refaktoryzowaniu kodu oraz o jego jakości. Z mojego punktu widzenia architektura jest emanacją relacji pomiędzy biznesem a IT. Jeśli relacja ta jest nieekologiczna, to żadna magiczna sztuczka nie poprawi designu. Dalej, refaktoryzacja jest dla mnie problem organizacyjnym, a nie technicznym, czy kompetencyjnym.

Co z wynika z powyższych dywagacji. Bardzo prosta rzecz. Możliwe staje się zastosowanie wytycznej: nie szukaj rozwiązania problemu na poziomie, na którym się on rozgrywa, przejdź poziom wyżej. Zatem po przekroczeniu wymienionego w H3 Punktu, jedyne co może poprawić jakość kodu to refaktoryzacja na poziomie organizacyjnym. Właśnie tak, zmień sposób zorganizowania prac nad projektem, a przełoży się to na poprawę architektury i jakości kodu. Możesz zacząć od przyjrzenia się następującym relacjom:
  • współpraca między zespołami deweloperskimi
  • współpraca między zespołami deweloperskimi a zespołami testującymi
  • współpraca między zespołami deweloperskimi a biznesem

Podam przykład, w pewnej firmie postanowiono wprowadzić Zespół ds. Trudnych i Beznadziejnych. Zajmowali się oni naprawianiem problemów, których nikt inny nie potrafił naprawić. Przez pewien czas rozwiązanie sprawdzało się świetnie. Przez pewien czas, bo po ośmiu miesiącach zaczęły pojawiać się następujące objawy:
  • najlepsi programiści znaleźli się w Zespole
  • najlepsi programiści nie mieli czasu na uczenie innych jak rozwiązywać trudne problemy
  • zwykli programiści przykładali mniejszą wagę do jakości kodu, bo wiedzieli, że Najlepsi wychwycą każde niedociągnięcie
  • Zespół Najlepszych miał coraz więcej pracy
  • zwykli programiści coraz mniej dbali o jakość kodu
  • jakość kodu drastycznie spadła
  • Najlepsi programiści nie mieli czasu na refaktoryzację, zwykli programiści nie potrafili jej przeprowadzić
Paradoksalnie można stwierdzić, że kod był pisany po to, aby Zespół ds. Trudnych i Beznadziejnych miał co robić:)


Najważniejsze założenia przestawione w tym artykule

  • Pierwszym celem strategicznej refaktoryzacji jest powstrzymanie postępującej degradacji w kodzie; drugim celem jest poprawa jego jakości
  • Aby zaproponować sensowny plan refaktoryzacji trzeba w pierwszej kolejności zrozumieć przyczyny powstania legacy code
  • Umiejętności programisty to kluczowa rzecz, która poprawia jakość kodu
  • Refaktoryzację można prowadzić na poziomie technicznym tylko do pewnego momentu, potem należy przenieść się na poziom organizacyjny

Tuesday, November 29, 2011

Bounded Context

Jasne, że nie będę od teraz pisał o DDD, bo już inni o tym ciekawie piszą. Chciałbym się tylko trochę uzewnętrznić w temacie, który szczególnie mnie poruszył, gdyż rozwiązuje dość poważny problem z architekturą. Jeśli nie nie zetknąłeś się z filozofią (w moim rozumieniu to bardziej sposób myślenia niż toolbox; taka obiektowość podniesiona level wyżej - na poziom architektury) DDD to na wstępie proponuję:

O jakim problemie mowa?

Powiedzmy, że rozpoznajemy, co też dzieje się domenie sklepu internetowego (nie wiem dlaczego to sklep zazwyczaj przychodzi na myśl:)). Powiedzmy, że dzieje się jak poniżej:
Jest to jakaś interakcja pomiędzy obiektami w dziedzinie.

Gdy zaczynamy modelować, naturalnie pojawiają się klasy takie jak: Zamówienie, Koszyk, itd. Powstaje model złożony z powiedzmy 30-50 klas. Wszystko wygląda super świetnie, ale zbiegiem czasu coś zaczyna nie grać. Pojawiają się następujące symptomy:
  • W związku z kolejnymi wymaganiami, do obiektów dochodzi coraz więcej atrybutów i metod
  • Programista implementujący przypadek użycia związany ze złożeniem zamówienia z trudem odnajduje się w gąszczu atrybutów i metod dodanych przez kogoś, kto implementował realizację Zamówienia
  • Niektóre obiekty są używane w tak wielu miejscach systemu, że jakakolwiek w nich zmiana ma spore skutki w innych odległych miejscach
Co więcej: jeśli z wymagań wynika jakaś konkretna zmiana w jednym miejscu systemu, to obija się ona głośną czkawką w zupełnie innym właśnie z powodu "reużywania" obiektów modelu.

Niby wszystko zostało przygotowane zgodnie z regułami sztuki, ale jednak wszyscy czują, że coś śmierdzi...tylko nie wiadomo z jakiej przyczyny.

Powód jest taki, że ten sam obiekt modelu używany jest wielu różnych kontekstach. Zamówienie wynikające z interakcji użytkownika z Koszykiem jest nieco inną sprawą niż Zamówienie realizowane przez magazyn.

"Bounded context should have a name so that you can talk about them"

Rację mieli starzy Indianie, gdy twierdzili, że jeśli poznasz czyjeś imię, zyskujesz nad tym kimś władzę. Nie inaczej jest z bounded contexts. Jeśli nie zauważymy i nie nazwiemy kontekstów użycia modelu, to mogą nam zrobić krzywdę. Nienazwane lubią się mieszać i dokładać do obiektów modelu rożnego rodzaju dodatki właściwe dla swej specyfiki. W skutek czego powstaje model-potworek, który w założeniu miał być super, a w wykonaniu i tak jest kolejną wersją big ball of mud.

Jeden model w jednym kontekście

W związku z dokonanymi odkryciami warto zalecać:
  • Nazwij kontekst, w którym może być wykorzystywany model. Już sama próba nazwania sprawi, że kontekstów pojawi się kilka (o ile tam są).
  • Jeden model może być wykorzystywany tylko w jednym kontekście (bounded context). Zamówienie w kontekście Koszyka to nie dokładnie to samo, co zamówienie w kontekście Magazynu.
  • Odizoluj poszczególne konteksty i właściwe im modele od siebie

W powyższych zasadach można dopatrzyć się echa Single Resposibility Principle. Z odpowiedzialności modelu powinien wynikać jednoznaczny kontekst jego użycia. Tyle, że Bounded Context definiuje to wprost i wyraźnie, a z zasady odpowiedzialności trzeba sobie wywnioskować.

Jeżeli w konsekwencji mamy zdefiniowanych kilka zwartych modeli o jednoznacznie określonych kontekstach użycia, to musi istnieć coś, co będzie potrafiło skonwertować obiekty z jednego kontekstu w drugi (jeśli jest taka potrzeba). W DDD ta potrzeba jest zaspokajana poprzez zdefiniowanie Context map w postaci zestawu usług-translatorów (w apachowych commonsach nazywanych transformerami). Ich odpowiedzialność to właśnie konwersja.

Podsumowując zyski z wprowadzenia konceptu Bounded Context:
  • Redukcja echa w systemie - zmiana w modelu w jednym miejscu nie skutkuje oddźwiękiem w innym
  • Zmiany wynikające z biznesu mają na poziomie architektury ograniczony do kontekstu zasięg
  • Można lepiej zorganizować prace nad projektem (tutaj DDD proponuje kilka pomysłów w zależności od tego jakie relacje występują pomiędzy kontekstami).

Monday, June 27, 2011

PEA(3): Wyodrębnianie user stories

To jest artykuł z serii Proces ewolucji architektury.

Po dość zgrubnym rozeznaniu się w środowiskowym kontekście systemu, jest czas na nieco więcej konkretów na temat funkcjonalności.

Część I - Przygotowanie


Dobór grupy
Do wyodrębnienia user stories świetnie nadają się spotkania focusowe. I ty pytanie z kim?
Najlepiej, aby grupa była różnorodna. Jednorodne grupy mają tendencję do "krzywienia" user stories. Na przykład programiści formułują je bardzo crudowo.

Aktorzy
Jeśli to możliwe, już na samym początku zidentyfikuj aktorów. Zadaj sobie pytanie Kto będzie wołał ten system?. W tym momencie masz już wstępnie rozpoznanych na następujących aktorów:
  • inne systemy - ponieważ poznałeś kontekst pracy systemu
  • użytkownik - na razie to dość ogólne sformułowanie, taki metaaktor, którego pojęcie będzie dokonkretyzowywać się w trakcie dalszych rozmów
  • dla porządku warto dorzucić jeszcze jednego: Time ; bojownicy o czystość UMLa mnie przeklną, ale wygodnie mieć tych aktorów dla spraw wyzwalanych czasem; jest to wygodne tylko i wyłącznie z tego powodu, aby nie bawić się w dodatkowe tabelki, gdy zajdzie potrzeba wysmarowania pięknego diagramu

Rysunek 1: Wstępny szkic aktorów w systemie
Wstępne nazwanie aktorów pomaga konkretnej zadawać pytania. Głównie o to tu chodzi. Konwencja nazewnicza Ustal ramową konwencję nazewniczą, zazwyczaj są dwie konkurencyjne alternatywy:
  • anglojęzyczna - działa proporcjonalnie do swobody językowej
  • polskojęzyczna - często jeden do jednego przekłada słownictwo binzesowe, ale w kodzie będą wychodzić zabawane pongliszowe kwiatki
Najkorzystniej jest promować taką konwencję nazewniczą, która zmienia jak najmniej przyzwyczajeń. Najczęściej słownictwo biznesowe jest zlepkiem z polskiego, angielskiego, a czasem i z innych języków i trudno je uspójniać. Zmiana organizacyjnej nowomowy trochę boli, ale przynosi niepomierne korzyści w postaci wzajemnego zrozumienia. Wypraktykowałem, że zamiast próbować udowadniać ludziom, że mają złe przyzwyczajenia, o wiele lepiej działa zadawanie pytań uwypuklające niejednoznaczność słownictwa. Na przykład: Co ma Pan na myśli mówiąc <<szkolenie>>. To <<program>> szkolenia, czy <<czynność>> prowadzenia szkolenia? Acha, rozumiem, czyli możemy mówić o szkoleniu <<możliwym do zorganizowania>> oraz <<zorganizowanym>> szkoleniu? Pułapką uspójniania słownictwa jest synonimia nomenklaturze organizacji. Śmiało można zaryzykować stwierdzenie, że każdy dział ma nieco inne nazwy na te same rzeczy i procesy.

Rysunek 2: różne nazwy na te same rzeczy, to jedna z większych trudności
Tego typu różnice dość szybko wychodzą, jeśli zadbasz o wspomnianą wcześniej różnorodność grupy. Kto nazywa ten rządzi Jak najszybciej ustalcie nazwę dla systemu. W przeciwnym razie, będziesz rozmawiać o oderwanych funkcjonalnościach, co sprawi, że zakres systemu będzie stale i nieograniczenie się powiększał. Najlepiej, aby nazwa wzięła się z rzeczywistości np.: kalendarz elektroniczny, aby występowała w informatyzowanym procesie. Łatwo wtedy eliminować wodotryski, zadając proste pytanie Czy dotychczas w kalendarzu również miał Pan możliwość dodawania awatarów? Nic nie mówiące i zbyt ogólne nazwy z kategorii Zintegrowany System Informatyczny, to otwarta furtka do wylądowania poza budżetem lub terminem lub oboma jednocześnie.

Część I - Prowadzenie sesji wyodrębniania user stories

Zasady ogólne Przed przystąpieniem do sesji wygodnie jest mieć jakiś szkic interfejsu użytkownika, żeby skupić uwagę osób. Jeśli ich nie ma to je rysuj. Rysowanie dobrze ogniskuje uwagę i często lepiej pozwala wyrazić myśli. Historycznie user storier spisywane są na małych kartkach. Powiedzmy sobie jednak szczerze, że arkuszem Excela o wiele lepiej się zarządza. Jak to zatem jest z tymi karteczkami? Z całego serca polecam karteczki (żółte, samoprzylepne :) ). Mają one tę wielką zaletę, że budują zaangażowanie interesariuszy. Osoby na spotkaniu są często przytłoczeni dużą liczbą obowiązków, nieco zmęczeni i teraz jeszcze muszą odbębnić z nami spotkanie. Mimo, że rzeczywiście chcą tego systemu, to nie chcą na niego poświęcać czasu - nielogiczne, ale prawdziwe. Jeśli będziesz zachęcać ludzi, aby sami pisali user stories na karteczkach i dodatkowo przyklejali je na fipcharcie, to "wciągną się" w spotkanie, będą bardziej kreatywni. Budując zaangażowanie, zwiększasz swoje szanse na wyodrębnienie rzeczywistych oczekiwań co do systemu zamiast ogólników, które czasem ktoś może chcieć Ci wcisnąć, żebyś tylko dał mu spokój. Zaangażowanie - to jest rzeczywisty cel karteczek. Rzecz jasna, że po spotkaniu możesz przepisać je do Excela, skoro w ten sposób łatwiej nimi zarządzać. Nie jestem fanem zbyt szybkiego wprowadzania narzędzi. Narzędzia (zwłaszcza takie, które narzucają swój proces pracy) częściej przeszkadzają niż pomagają. Fanom narzędzi chętnie polecam Scrum Do. Jest bardzo grywalne, zawiera wszystko co potrzeba, a jednocześnie jest kompletnie nieinwazyjne. Minusem jest to, że działa on-line. Formułowanie historyki Ogólny algorytm prowadzenia sesji wygląda tak:
  1. Ustal uwagę na pierwszym ekranie
  2. Zadaj pytanie: Co można zrobić na tym ekranie?
  3. Skłoń ludzi, aby sformułowali swoje oczekiwania w następujący sposób: Jako <<kto?>>mogę <<funkcjonalność>>, aby <<po co?>>
Struktura zdania, za pomocą którego formułujemy historyjki, jest bardzo chytra. Każda z części tego zdania ma swój ściśle określony cel. Przyjrzyjmy się jej bliżej.
  1. Jako <<kto?>> - tu konkretyzujemy aktorów (role); funkcjonalności nie są zawieszone w powietrzu; dzięki przyporządkowaniu historyjek do ról, łatwiej na późniejszym etapie ogarnąć zasady bezpieczeństwa dostępu do systemu
  2. mogę <<funkcjonalność>> - tu precyzujemy konkretną funkcjonalność, do której dany użytkownik ma mieć dostęp
  3. aby <<po co?>> - ta bardzo ważna część, pomaga użytkownikom uświadomić sobie potrzeby oraz problemy, które zamierzają zaspokoić i rozwiązać za pomocą tej funkcjonalności; dodanie tej części eliminuje sporo niepotrzebnych funkcjonalności, bo gdy ludzie zaczynają zastanawiać się po co coś będzie potrzebne, to czasem dochodzą do wniosku, że po nic
Ogólniki i szczególiki Podczas spisywania historyjek, możemy otrzymać efekt dwojakiego rodzaju:
konkretne user stories
np.: Jako U mogę zobaczyć listę swoich zamówień, aby wiedzieć co już otrzymałem, a czego jeszcze nie; do takich sformułowań zmierzamy podczas pracy z interesariuszami
niekonkretne user stories
np. Jako P mogę zarządzać dokumentami?; słowo "zarządzać" niewiele mówi o tym co właściwie należy zrobić, być może kryje się za tym jakiś ogromniasty epic;

Prawdopodobnie będziesz mieć chęć, aby drążyć niekonkretne historyki, gdy tylko użytkownicy je sformułują. Kłopot w tym, że prawdopodobnie oni sami jeszcze nie przemyśleli co to konkretnie ma być. Proponuję, by chwilowo zostawić je w spokoju. Zapisz je i wróć do nich później, już po sformułowaniu wszystkich user stories.

Dodatkowe porady
Podczas sesji ludzie spierają się o to, co właściwie ma robić dana rola i dość szybko zaczynają roztrząsać kwestie techniczne. Nie daj się wciągnąć w tę dyskusję, ucinaj ją natychmiast i prowadź spotkanie dalej. Na tym etapie eksploracja "w szerz" zamiast w "głąb" zagadnienia, pozwoli Ci szybciej przyswoić sobie wiedzę domenową.

Tego typu spotkania wygodnie jest prowadzić w dwie osoby. Jedna aktywnie moderuje rozmowę, druga nieco mniej zaangażowana zachowuje big picture i wkracza w razie kłopotów.

Wednesday, April 20, 2011

Gadżety Javy

Jeszcze parę lat temu mówiło się, że Spring i EJB wcale ze sobą nie konkurują. Każde z nich miało swoje obszary zastosowań, w których się sprawdzało, a drugiemu nie wchodziło w paradę. Wyhodowany na bolączkach EJB2 Spring Framework, rozwijany przez genialnych programistów z Interface21, świadomie pozbawiony był nadmiarowych bajerów EJB, przez co wypełnił tą niszę, w której potrzeba było minimalistycznego kontenera.

Z biegiem czasu dochodziły coraz to nowe dodatki: kolejne moduły, kolejne podprojekty, własny serwer aplikacji, SpringSource ToolSuite. Czy wciąż Spring i EJB mają swoje obszary stosowalności? Czy może stały się nawzajem dla siebie alternatywami? Porównajmy:

SpringFrameworkEJB3.*
bierzesz, co chcesz; w razie potrzeb dokładasz kolejne modułybierzesz wszystko albo nic; klejony na siłę koncept profili moim zdaniem tak bezsensowny, że nawet nie warto się rozpisywać
częste wydaniaw bólach i na drodze kompromisów rodząca się specyfikacja jest sporo w tyle za nowinkami ze świata OpenSource
nastawiony na bezinwazyjne sklejanie fragmentów aplikacji oraz na integrowanie się z bibliotekami i frameworkami, które już istnieją i dobrze się sprawdzająmityczna przenośność aplikacji pomiędzy serwerami
staje się coraz cięższy; może nie sam kontener ale włączając kolejne ficzery robi się coraz ciężejzmierz w stronę odchudzania (JPA poza kontenerem, koncept profili)
Projekt OpenTerracotta spierający skalowanie aplikacji opartych o Springakontener EJB dostarcza możliwości skalowania aplikacji
można tworzyć aplikacje wielowątkowezakaz własnoręcznego korzystania z wątków; wątkami zarządza wyłącznie kontener
brak standardu dot. zarządzania aplikacją; istnieją projekty SpringSource dedykowane do tego celuspójny sposób zarządzania aplikacją


Okazuje się, że Spring wsparty bibliotekami zewnętrznymi potrafi zapewnić dokładnie te same funkcjonalności, że EJB. Być może jeszcze jedną różnicą jest to, że w przypadku Spring trzeba dokładnie wiedzieć jaki efekt chce się uzyskać, gdyż oferuje on bogactwo różnych opcji na rozwiązanie tego samego problemu i trzeba wiedzieć, która jest najodpowiedniejsza dla naszego projektu. W przypadku EJB, mamy wszystko out of the box.

Kontynuując tyradę narzekania
Kontynuując tę tyradę narzekanie trzeba wspomnieć jeszcze wiele genialnych frejmłorków i bibliotek, które świetnie wyglądają w 10 minutes tutorial, rozwiązują parę problemów i wprowadzają szereg nowych.
Wymieniam parę wad:

JSFciężkie drzewo komponentów, pokręcony mechanizm renderowania; wersja 1.2 - toporna i wymagająca paru dodatków, aby sensownie pracować; wersja 2.0 - niby fajnie ale poużywamy-zobaczymy; RichFaces 4.0 wspierajace JSF 2.0 już jest
JPAwolno rozwijająca się specyfikacja, plus wszystkie wady O/RMów
GWTdostawca monopolista, ciężki klient, brak standardu, po stronie widoku obsługiwana jest tylko część maszyny wirtualnej
Struts2"prawie" wsparcie dla Ajaxa, koszmarne adnotacji
Spring WebFlowfajny, gdy GUI ma charakter procesowo-kreatorowy, poza tym przerost formy nad treścią
Oracle ADFw tutorialach genialny, w użyciu prowadzi do prawie takiego samego bałaganu w kodzie jak przy bezmyślnym klikaniu w Borland Delphi Buildera

Co zamiast w/w?
Jakie mamy zatem mamy alternatywy? Co można używać zamiast powyższych rozwiązań. Spotkałem się z następującymi konfiguracjami:
  • Stripes + dojo + Hibernate - wychodzimy z założenia, że nie ma co liczyć na automagiczne ajaxy i jeśli chce się mieć fajne GUI to trzeba w JavaScript popracować
  • Freemarker + SpringMVC + myBatis - całkowita rezygnacja z JSP oraz pogodzenie się z faktem, że schemat bazy jest czasem tak prosty, że nie ma co przekombinowywać
  • Oracle Forms, PowerBuilder - w architekturze dwuwarstwowej
  • PHP - tak po prostu... to o wiele bardziej dojrzała technologia, niż jawowcom może się wydawać, mają nawet swoje automagiczne frejmłorki


Do czego właściwie nadaje się się JEE
Nie chcę za bardzo wyrokować, ale JEE rozważałbym wtedy, gdy...nie ma innego wyjścia, gdy z powodów historycznych (zastany stan systemu), ludzkich (umiejętności programistów) albo politycznych trzeba się decydować na tą konkretną technologię. Bardzo, ale to bardzo byłbym ostrożny, przy rozważaniu JEE dla aplikacji webowej. Dzisiaj jestem zdania, że do aplikacji strice webowych (i nic ponad to) JEE się nie nadaje: zbyt wolne, zbyt kosztowne (chociażby ze względu na wynagrodzenia programistów).

Do czego się nadaje. Oprócz wymienionych na wstępie powodów można jeszcze dorzuć szeroko rozumiany backend przy tworzeniu dużych i długotrwałych projektów o bogatym modelu - wtedy można doszukiwać się korzyści w inwestowaniu w JEE.



P.S. Wpis wyraża prywatne myśli autora, a autor nie zarzeka się, że myśli tych kiedyś nie zmieni:)

Tuesday, April 5, 2011

Strażnik architektury i ewangelista

Zauważyłem pewną cechę różnicującą projekty, w których kod woła o pomstę do nieba, od tych, które są bardzo przyzwoicie napisane. Różnicą, którą mam na myśli nie jest bynajmniej skala projektu, skomplikowanie domeny, język programowania, czy nawet wiek projektu. Zauważyłem, że w zespołach, w których kod jest dobrze utrzymany objawia się osoba, która nakręca cały ten pęd ku ciągłemu doskonaleniu. Nazwałem go Strażnikiem architektury.

Nie posługuję się nomenklaturą Scotta Amblera, który mówi o Właścicielu architektury, gdyż sądzę, że chodzi o nieco inną rolę. Mimo koncepcji Właściciela architektury, jako roli, a nie stanowiska (zapewne z inspiracji Product Ownerem) trudno uciec od skojarzeń, że właściciel coś posiada. Tego typu rola, bardzo szybko przekształca się w klasyczne stanowisko architekta, który jest wyrocznią w sprawie architektury, od czego Scott Ambler chciał uciec.

Strażnik architektury jest jednym z członków zespołu, który ma "parcie" na ciągłe doskonalenie projektu oraz ewangelizuje zespół w zakresie jakości tworzonego kodu. Zaobserwowałem, że zazwyczaj Strażnik architektury jest inicjatorem i koordynatorem następujących aktywności w zespole:
  • wprowadzanie standardu kodowania
  • zmiana/wprowadzanie systemu kontroli wersji/zmian
  • wprowadzenie code review
  • wprowadzenie procesu continuous integration
  • wprowadzenie/zmiana podejścia tworzenia kodu np:. DDD
  • wprowadzenie/zmiana technologii/frameworka
  • tworzenie warstw abstrakcji na niektóre z używanych bibliotek, w celu uniezależnienia się od nich
  • zmiana architektury w celu poprawienia wydajności (i nie chodzi tylko do dopisanie paru SQLi i założenie paru indeksów)

Zazwyczaj Strażnik architektury objawia się sam. Po prostu jest ktoś taki w zespole, od kto stopniowo staje się autorytetem dla innych. Uczy ich, prowokuje do wyjścia poza utarte schematy, stymuluje do rozwoju. Zazwyczaj pomaga rozstrzygnąć sporne kwestie dotyczące technicznej strony projektu. Doradza i nieformalnie akceptuje nowe pomysły albo je odrzuca (albo jak mawiają znajomi z pewnego sympatycznego zespołu "spsikuje" pomysły ;) )

Kilka punktów nt. zaobserwowanej charakterystyki Strażnika architektury.
  • jest pasjonatem
  • poświęca wolny czas na czytanie blogów, literatury, rozpoznawanie technologii
  • testuje nowe rozwiązania (często projekcie, w którym uczestniczy)
  • często staje się liderem z nadania (poprzez stanowisko) i bywa, że to go "zabija" bo tonie w papierkach, budżetach i spotkaniach
  • mimo szeregowego stanowiska z jego zdaniem management się liczy i potrafi przelobbować praktycznie co tylko chce
  • szybko wyciąga wnioski z pomyłek, ale się nie zraża
  • jest motywowany głównie "na cel"

Pojawia się jednak pewne niebezpieczeństwo. Pęd Strażnika architektury do doskonalenia projektu jest niemal niepohamowany. Niepohamowany do tego stopnia, że może łatwo przekroczyć dopuszczalną tolerancję biznesu oraz wytrzymałość zespołu. Istnieje duże prawdopodobieństwo, że za przyczyną niekontrolowanego Strażnika architektury zespół będzie co miesiąc zmieniał frameworki, a co pół roku technologię.

Kontrolowanie Strażnika przez management bardzo szybko zachęci go do poszukania nowej pracy, gdzie będzie mógł realizować swoje pomysły. Rozwiązanie tej kwestii, podpowiedział mi układ personalny pojawiający się nieraz w zespołach. Otóż, w sprawnie funkcjonującym zespole, prócz Strażnika architektury pojawia się jeszcze jego przeciwwaga. Nazwijmy go Weryfikatorem. Charakterystyka Weryfikatora to:
  • jest równie kompetentny technicznie co Strażnik architektury
  • bardziej niż nowinki technologicznie ceni sobie bezpieczeństwo
  • jest skrupulatny, metodyczny i niesamowicie cierpliwy
  • potrafi długo dyskutować ze Strażnikiem architektury i obalać jego teorie jeśli uzna je za niewystarczająco dobrze uzasadnione
  • jest motywowany głównie "od problemu"


Strażnik architektury oraz Weryfikator stanowią główną oś zespołu dzięki, dzięki któremu projektu ewoluują w równomiernym tempie. Żaden z nich nie jest nadmiernie, czy sztucznie kontrolowany. Raczej wspólnie wypracowują rozwiązania, czerpiąc jednocześnie wiele satysfakcji z pracy.

Podsumowując: jeśli chcesz mieć dobry zespół i dobrze napisany projekt, to proponuję następujący przepis:
  1. Zatrudnij Strażnika architektury oraz Weryfikatora o wskazanych wyżej cechach - ci muszą być rewelacyjni
  2. Zatrudnij pozostałych członków zespołu - ci powinni być dobrzy
  3. Pozwól im działać

Mnóstwo pytań nasuwa się w związku z tym pomysłem, chociażby: jako to zrobić?, skąd wziąć ludzi?, jak to zrobić najtaniej?. TODO.... :)

Saturday, March 19, 2011

Generowanie danych dla widoku

Spotkałem się niedawno z ciekawym rozwiązaniem dotyczącym sposobu generowania danych dla widoku. Warto je opisać.

O co chodzi?
W większości aplikacji musimy dostarczać do widoku różnego dane by je zaprezentować użytkownikowi. Mogą być to dane, na których użytkownik bieżąco pracuje i które widzi na stronie albo dane służące go produkowania raportów, z którymi użytkownik nie wchodzi w interakcję.

Często wystarcza nam przesyłanie do widoku obiektów detached. Przy złożonych modelach domenowych przesyłanie fragmentów modelu domenowego nie jest wygodne. Chociażby z tego względu, że widok często wymaga danych przekrojowych, pochodzących z różnych miejsc modelu. Radzimy sobie wtedy za pomocą obiektów DTO tworzonych na potrzeby konkretnych widoków (Rysunek 1).






Rysunek 1: Generowanie DTO z modelu domenowego

W zależności od skomplikowania obiektów transferowych, generowanie ich można umieścić albo w samych obiektach domenowych albo scentralizować w usłudze Transfer Object Assembler.

Gdy ilość sposobów prezentowania danych dramatycznie wzrasta (raporty, ekrany), okazuje się, że kod związany z obiektami transferowymi zaśmieca projekt i coraz trudniej go utrzymywać.

Pierwsze rozwiązanie
Pierwszy pomysł to przeniesienie odpowiedzialności generowania obiektów DTO na SQL np. z wykorzystaniem biblioteki myBatis. Pamiętajmy, że chodzi o obiekty tylko do odczytu.




Rysunek 2: ORM dla modelu domentowego myBatis dla DTO

Powoduje to dwa problemy. Po pierwsze: znów w miarę rozrastania warstwy widoku a wraz z nią rożnych sposobów (przekrojów) prezentowania danych, zarządzanie zapytaniami SQL nawet umieszczonymi w plikach XML jest uciążliwe. Po drugie pojawia się dualizm: z jednej strony ORM zarządza modelem domenowym, z drugiej wcinamy się gdzieś z boku SQLem, aby pobrać dane dla widoku.

Mapowanie widoków bazodanowych
Rozwiązanie, z którym się spotkałem, polega na zepchnięciu generowania danych przekrojowych bazę danych i reprezentowania ich w postaci widoków bazodanowych. Obiekt DTO mapujemy jako encje na w zdefiniowane w bazie widoki.
W usłudze, dzięki której pobieramy dane z bazy udostępniamy wyłącznie możliwość odczytu.

Ciekawe jakby można nazwać obiekt taką usługę. DAO, Repository? Jakoś kłóci się z tym, co zwyczajowo rozumiemy przez tego typu usługi. Wydaje mi się, że dobrą (na razie ogólną) nazwą będzie PresentationDataService.




Rysunek 3: ORM mapuje widoki bazodanowe

Urzekająca jest symetryczność tego rozwiązania: to co obiektowe jest po stronie obiektów, to co bazodanowe po stronie bazy danych, a ORM wykorzystany jako pomost pomiędzy tymi dwoma światami.

Tymczasowa niespójność danych
W opisanym rozwiązaniu kluczowym aspektem jest wydajność. Po pierwsze: umieszczamy dane dla prezentacji w widoku zmaterializowanym.
Wydajność można dramatycznie poprawić jeśli zgodzimy się na tymczasową niespójność danych. Na przykład: użytkownik dodaje nowego pracownika i otrzymuje komunikat: "Twoje zmiany pojawią się w systemie w ciągu 10 minut.". Okresowe przeliczanie widoku zmaterializowanego jest zdecydowanie wydajniejszym rozwiązaniem niż robienie tego na każde żądanie użytkownika.
Oczywiście jeśli baza danych nie wspiera widoków zmaterializowanych to używamy tabeli tymczasowej i sami klepiemy kod aktualizujący.

Przeszukiwanie dużych ilości danych
Poniższe rozwiązanie dotyczy sytuacji, gdy mamy w bazie kilka milionów np.: klientów oraz złożone kryteria wyszukiwania. Mówiąc o złożonych kryteriach mam na myśli zmienną liczbę parametrów, po których będziemy wyszukiwać (w zależności od potrzeb wynikających z sytuacji biznesowych). Przy stałej liczbie parametrów można stworzyć odpowiednią ilość zapytań QL. Przy zmiennej liczbie parametrów najwygodniej posługiwać się obiektami Cirteria udostępnianymi przez dany ORM. Znów kluczową sprawą jest wydajność przeszukiwania.

Tabela pośrednia z kryteriami wyszukiwania
Pomysł polega na utworzeniu w bazie danych pomocniczej tabeli (widoku zmaterializowanego), która z jednej strony zawiera iloczyn kartezjański wszystkich atrybutów wyszukiwanego obiektu, a z drugiej namiar na odpowiedni wiersz w tabeli związanej z właściwym obiektem, który spełnia kryteria wyszukiwania.





Rysunek 4: Pomocnicza tabela z kryteriami wyszukiwania

Pytanie otwarte: Czy pośrednia tabela kryteriów i odpowiednie jej poindeksowanie rzeczywiście przyśpieszy wyszukiwanie obiektów?